0

I am sending a POST request to a URL. It works correctly in python but in php-curl, I always get a bad request error (i.e. my POST data is not as expected by the server)

Python code: (works correctly. 200 OK)

import httplib, urllib, json

def SendRequest(param1):
    url = "api.xyz.com"
    body = {"version": "1.0", "data":[{"param1":param1, "age":35}]}
    jsonBody = json.dumps(body)

    headers = {"Content-type": "application/json",
               "Accept": "application/json; charset=utf8"}
    conn = httplib.HTTPConnection(url)
    conn.request("POST", "/api/?client_id=xx-xx-xx", jsonBody, headers)
    response = conn.getresponse()

php-cURL code (does not work. 406 Bad request error)

function sendCurlRequest($param1)
{
    $payload = preparePayload($param1); 
    $url = "http://api.xyz.com/api/?client_id=xx-xx-xx"; 
    $jsonResponse = executePOSTRequest($url, $payload);
    $response = json_decode($jsonResponse);
}

function preparePayload($param1)
{
    $payload = array("version" = "1.0",
                     "data" => array(array("param1" => $param1,
                                           "age" => 35
                                          )
                                    ),
                    );

    $jsonPayload = json_encode($payload);
    return $jsonPayload;
}

function executePOSTRequest($url, $payload)
{
    $newlines = array("\r", "\n", " ");
    $url = str_replace($newlines, '', $url);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt ($ch, CURLOPT_HTTPHEADERS,array('Content-Type:application/json; Accept: application/json; charset=utf8'));

    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($ch);
}

I know there is some mistake in my use of cURL. Please suggest.

Update: Use of double array in php is because server expects the JSON string (POST payload) in this format

{
"version": "1.0",
"data":
[{"param1":name, "age":35},{"param1":name,"age":60}]
}

In python, I am using list of dict; in php I think it is array of arrays only.

hakre
  • 193,403
  • 52
  • 435
  • 836
vivek.m
  • 3,213
  • 5
  • 33
  • 48
  • I'm pretty sure your problem is that the original Python code is sending a pure JSON body, and the PHP version is posting the various values as POST fields. I don't know how to send a pure JSON body but I'm sure the answer is somewhere here on SO – Pekka Oct 30 '10 at 11:35
  • Pekka, thx fo your comment but I think in both cases, I am sending a JSON string as POST data. Do you mean that in php, my JSON string is being broken into pieces and each field sent as form-fields? that cannot be the case. – vivek.m Oct 30 '10 at 12:48

3 Answers3

1

In a case like this, I find that it's always better to look at what goes over the wire instead of trying to puzzle out what might be wrong with the code.

My advice:

  • Create a little test server using the python BaseHTTPServer and derive a class from BaseHTTPRequestHandler that doesn't do anything but provide an override of the do_POST method

  • Your do_POST should just dump the headers and POST body it receives to file

  • Point your code samples at this little test server and then diff the files that it creates. I'm guessing that if the answer isn't immediately obvious, you'll at the very least get one or more useful clues to stomp this problem out.

bgporter
  • 35,114
  • 8
  • 59
  • 65
  • Thanks for the sound advice. It will be of great help in future problems as well. Finally, I did a POST to local server through php and python and compared the differences in headers and data. I was making some mistakes in using curl. Thanks. – vivek.m Oct 31 '10 at 17:28
  • Excellent! Glad to be able to help. – bgporter Oct 31 '10 at 23:41
1

I'm not sure if it causes your problem, but I do see a difference between the two: In the python code you send two header lines: Content-Type and Accept. While in the PHP code, they are a single line.

Michielvv
  • 336
  • 4
  • 13
  • no, that does not cause a problem. I have wrestled with curl for quite a long time. I have tried sending Content-Type and Accept as two separate lines. No difference. – vivek.m Oct 30 '10 at 13:53
  • 1
    Ok, are you also aware that the option should be named: CURLOPT_HTTPHEADER without the S at the end? So: curl_setopt ($ch, CURLOPT_HTTPHEADER,array('Content-Type:application/json;','Accept: application/json; charset=utf8')); – Michielvv Oct 30 '10 at 14:33
  • Hey, Michielvv, nice catch. I did not notice the extra 'S'. When I did a POST to local server, only then I saw it in apache error log. Thanks. :-) Also, as you said, Content-Type and Accept should be different elements in array. Merging them as a single line is WRONG. Thanks. – vivek.m Oct 31 '10 at 17:31
1

You are missing the parameter index - cURL POST fields need to be sent as a query string or as an array.

Either:

curl_setopt($ch, CURLOPT_POSTFIELDS, 'payload=' . urlencode($payload));

Or

curl_setopt($ch, CURLOPT_POSTFIELDS, array('payload' => $payload));

Replace payload with the name of the parameter expected by the API to hold the JSON string.

If the endpoint API is not expecting a parameter, then it should work - it just won't appear in the POST array in your test script. You can fetch the body of the POST contents using

$data = file_get_contents("php://input");
var_dump($data);
Eran Galperin
  • 86,251
  • 24
  • 115
  • 132
  • yes, I have got your point. But in python, I am not using any parameter to hold JSON string. Also, the API docs do not mention any such parameter. All it mentions is "format the request as JSON string and submit it as POST data to given URL". Is this cURL limitation? – vivek.m Oct 31 '10 at 14:20
  • In that case, it should've worked as you've written it before. Did you try it with the actual API or just with your testing script? I added a way for you to test direct POST content with the $_POST array – Eran Galperin Oct 31 '10 at 14:57
  • yes, Eran. thanks. I was following the advice of bgporter to post on local server through php and python and check the difference between the two. That's how I got into empty _POST problem. http://stackoverflow.com/questions/4060345/post-is-empty-in-php – vivek.m Oct 31 '10 at 17:21
  • My API is working fine now. I was checking response header through get_header($url, 1) php function. And it was always returning 406 error. The correct way is to use "curl_getinfo($ch)". It gives 200 ok. Another mistake was as pointed by Michielvv. (using CURLOPT_HTTPHEADER instead of HEADERS :) ) – vivek.m Oct 31 '10 at 17:25