PHP cURL POST returns a 415 - Unsupported Media Type

44,018

Solution 1

Problem solved! Here's the issue:

Sending an associative-array of headers DOES NOT WORK with cURL. There are several forums scattered around that show examples using an associative array for headers. DON'T DO IT!

The correct way (which is also scattered around the internets, but that I'm too dense to have noticed) is to construct your header key/value pairs as strings, and pass a standard array of these strings when setting the CURLOPT_HTTPHEADER option.

So in summary,

WRONG:

$headers = array(    "Accept-Encoding" =>    "gzip",
                     "Content-Type" =>       "application/json",
                     "custom_header_1" =>    "test011",
                     "custom_header_2" =>    "test012",
                     "custom_header_3" =>    "test013");

RIGHT:

$headers = array(    "Accept-Encoding: gzip",
                     "Content-Type: application/json",
                     "custom_header_1: test011",
                     "custom_header_2: test012",
                     "custom_header_3: test013");

I hope this comes in handy to some other noble doofus down the road before they waste as much time debugging as I did.

If I had to guess, I would assume that the same rule applies to the POST body key/value pairs as well, which is why @drew010 's comment about using http_build_query() or json_encode() to stringify your message body is a great idea as well.

Thanks to everyone for your very useful comments, and for you time and consideration. In the end, a side by side comparison of the http traffic (captured via Wireshark) revealed the issue.

Thanks!

Solution 2

I think the problem is that you are passing an array as the CURLOPT_POSTFIELDS option. By passing an array, this forces the POST request to use multipart/form-data when the server is probably expecting application/x-www-form-urlencoded.

Try changing

curl_setopt($request, CURLOPT_POSTFIELDS, $this->body);

to

curl_setopt($request, CURLOPT_POSTFIELDS, http_build_query($this->body));

See http_build_query for more information and also this answer: My cURL request confuses some servers?

Solution 3

This worked for me

 $data ="data";

 $headers = [
                "Content-Type: application/json",
                "X-Content-Type-Options:nosniff",
                "Accept:application/json",
                "Cache-Control:no-cache"
            ];

  $auth =  $USER . ":" . $PASSWORD;


 $curl = curl_init();
        curl_setopt($curl,CURLOPT_URL, $url);
        curl_setopt($curl,CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl,CURLOPT_ENCODING, "");
        curl_setopt($curl,CURLOPT_MAXREDIRS, 10);
        curl_setopt($curl,CURLOPT_TIMEOUT, 0);
        curl_setopt($curl,CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_1_1);
        curl_setopt($curl,CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data)); 
        curl_setopt($curl, CURLOPT_USERPWD,  $auth);  
        curl_setopt($curl,CURLOPT_HTTPHEADER, $headers);

         $result = curl_exec($curl);

Solution 4

I had the same problem and I fixed changing the header.

My code:

$authorization = 'authorization: Bearer '.trim($apiKey);
$header = [
'Content-Type: application/json',
$authorization
];
curl_setopt($session, CURLOPT_HTTPHEADER, $header);

I don't know why the array function doesn't work :

curl_setopt($session, CURLOPT_HTTPHEADER, array('Content-Type: 
application/json',
$authorization));
Share:
44,018
Ian
Author by

Ian

Updated on June 24, 2020

Comments

  • Ian
    Ian about 4 years

    I've got a simple PHP script that sends an HTTP POST request via cURL, and expects a json string in response (would have loved to use an existing library like pecl_http/HTTPRequest for this, but can't). The call consistently fails with a 415 error - Unsupported Media Type. I think I'm not configuring cURL correctly, but after much searching, I can't find out what I'm doing wrong. Here's some code:

    class URLRequest
    {
        public $url;
        public $headers;
        public $params;
        public $body;
        public $expectedFormat;
        public $method;
    
        public function URLRequest($aUrl, array $aHeaders, array $aParams, $aFormat = "json", $isPost = false, $aBody = "+")
        {
            $this->url = $aUrl;
            $this->headers = $aHeaders;
            $this->params = $aParams;
            $this->expectedFormat = $aFormat;
            $this->method = ($isPost ? "POST" : "GET");
            $this->body = $aBody;
    
        }
    
        public function exec()
        {
    
            $queryStr = "?";
            foreach($this->params as $key=>$val)
                $queryStr .= $key . "=" . $val . "&";
    
            //trim the last '&'
            $queryStr = rtrim($queryStr, "&");
    
            $url = $this->url . $queryStr;
    
            $request = curl_init();
            curl_setopt($request, CURLOPT_URL, $url);
            curl_setopt($request, CURLOPT_HEADER, 1);
            curl_setopt($request, CURLOPT_HTTPHEADER, $this->headers);
            curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
            //curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false);
    
            if($this->method == "POST")
            {
                curl_setopt($request, CURLOPT_POST, 1);
                curl_setopt($request, CURLOPT_POSTFIELDS, $this->body);
    
                //this prevents an additions code 100 from getting returned
                //found it in some forum - seems kind of hacky
                curl_setopt($request, CURLOPT_HTTPHEADER, array("Expect:"));
            }
    
            $response = curl_exec($request);
            curl_close($request);
    
            preg_match("%(?<=HTTP/[0-9]\.[0-9] )[0-9]+%", $response, $code);
    
            $resp = "";
            if($this->expectedFormat == "json")
            {
                //parse response
            }
            elseif($this->expectedFormat == "xml")
            {
                //parse response
            }
    
            return $resp;
    
        }
    }
    
    
    $url = "http://mydomain.com/myrestcall";
    
    $query = array( "arg_1" =>      "test001",
                    "arg_2" =>      "test002",
                    "arg_3" =>      "test003");
    
    $headers = array(    "Accept-Encoding" =>    "gzip",
                        "Content-Type" =>       "application/json",
                        "Accept" =>             "application/json",
                        "custom_header_1" =>    "test011",
                        "custom_header_2" =>    "test012",
                        "custom_header_3" =>    "test013");
    
    $body = array(  "body_arg_1" =>      "test021",
                    "body_arg_2" =>     array("test022", "test023"), 
                    "body_arg_3" =>     "test024"); 
    
    
    $request = new URLRequest($url, $headers, $query, "json", true, $body);
    
    $response = $request->exec();
    

    ...and the response:

    HTTP/1.1 415 Unsupported Media Type
    Server: Apache-Coyote/1.1
    X-Powered-By: Servlet 2.5; JBoss-5.0/JBossWeb-2.1
    Content-Type: text/html;charset=utf-8
    Content-Length: 1047
    Date: Mon, 18 Jun 2012 16:30:44 GMT
    
    <html><head><title>JBoss Web/2.1.3.GA - Error report</title></head><body><h1>HTTP Status 415 - </h1><p><b>type</b> Status report</p><p><b>message</b> <u></u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().</u></p><h3>JBoss Web/2.1.3.GA</h3></body></html>
    

    Any insights or ideas?

    Thanks in advance!

  • Ian
    Ian about 12 years
    Thanks for the suggestion - same response though. I'll keep this in mind moving forward though. You make a good point.
  • drew010
    drew010 about 12 years
    Sniffing the entire request in Wireshark may be helpful so you can see the HTTP request and response. Are they expecting you to post JSON data rather than urlencoded data?
  • Ian
    Ian about 12 years
    It is expecting JSON, and per my response to Boris' comment, I'm going to follow your council, Wireshark my call along with the call sent from Poster, and compare the two.
  • drew010
    drew010 about 12 years
    In that case you may need to set CURLOPT_POSTFIELDS to json_encode($this->body) and confirm that the content-type header going out is not being overridden by curl back to anything other than application/json
  • Ian
    Ian about 12 years
    Checking out what wireshark has to say on the matter - thought I had it, and made this comment, then had to edit it to remove erroneous findings...
  • drew010
    drew010 about 12 years
    Can you confirm that the if ($this->method == 'POST') block is being entered? Also, when you set the Expect header in the post block, you are clearing all the headers that were previously set. Even so, I don't think that should revert the request back to GET though.
  • Felipe N Moura
    Felipe N Moura over 5 years
    Damn! I was trying EVERYTHING and literally found dozens of different pages and tutorials showing how to set headers with key=>value and it simply wasn't working! THANKS, IAN!
  • Anjana Silva
    Anjana Silva over 4 years
    Thanks for saving possible hours/days of frustration mate :)