10

Why doesn't this simple code POST data to my service:

import requests
import json

data = {"data" : "24.3"}
data_json = json.dumps(data)
response = requests.post(url, data=data_json)
print response.text

And my service is developed using WCF like this :

  [OperationContract]
  [WebInvoke(Method = "POST", UriTemplate = "/test", ResponseFormat =    
      WebMessageFormat.Json,RequestFormat=WebMessageFormat.Json)]
  string test(string data );

Note: If is remove the input parameter data everything works fine, what may be the issue.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Suave Nti
  • 3,721
  • 11
  • 54
  • 78
  • When you say it doesn't work, what exactly do you mean? From the last "Note" it sounds like it's more than just "ignores my data" (because _without_ the data "everything works fine"), but then exactly what _does_ happen? – abarnert Dec 18 '12 at 21:36
  • Well I think I found the issue. It is with the {"data" : "24.3"} .. for some reasons I need to enclose that as a string thats when my request is going through.. like this "{data : 24.3}" .. anyone care to explain reasons ? – Suave Nti Dec 18 '12 at 21:40
  • Have you tried `data = {"data": 24.3}` (note: a float, not a string)? I don't know WCF, but here's another interpretation: `string test(string data)` might mean that your server expects a single string as an input (`data_json = '"something"'` (note: it is a Python string that contains json text that represents json string)), and it doesn't expect a json object. Strictly speaking "application/json" must represent either a json object (e.g., `data_json = '{"a", 1}'`) or a json array (e.g., `data_json = '[1,2,3]'`) therefore it is incorrect to accept just a string. – jfs Dec 18 '12 at 22:14

1 Answers1

28

You need to set the content type header:

data = {"data" : "24.3"}
data_json = json.dumps(data)
headers = {'Content-type': 'application/json'}

response = requests.post(url, data=data_json, headers=headers)

If I set url to http://httpbin.org/post, that server echos back to me what was posted:

>>> import json
>>> import requests
>>> import pprint
>>> url = 'http://httpbin.org/post'
>>> data = {"data" : "24.3"}
>>> data_json = json.dumps(data)
>>> headers = {'Content-type': 'application/json'}
>>> response = requests.post(url, data=data_json, headers=headers)
>>> pprint.pprint(response.json())
{u'args': {},
 u'data': u'{"data": "24.3"}',
 u'files': {},
 u'form': {},
 u'headers': {u'Accept': u'*/*',
              u'Accept-Encoding': u'gzip, deflate, compress',
              u'Connection': u'keep-alive',
              u'Content-Length': u'16',
              u'Content-Type': u'application/json',
              u'Host': u'httpbin.org',
              u'User-Agent': u'python-requests/1.0.3 CPython/2.6.8 Darwin/11.4.2'},
 u'json': {u'data': u'24.3'},
 u'origin': u'109.247.40.35',
 u'url': u'http://httpbin.org/post'}
>>> pprint.pprint(response.json()['json'])
{u'data': u'24.3'}

If you are using requests version 2.4.2 or newer, you can leave the JSON encoding to the library; it'll automatically set the correct Content-Type header for you too. Pass in the data to be sent as JSON into the json keyword argument:

data = {"data" : "24.3"}
response = requests.post(url, json=data)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Tried with `Content-type` earlier but no luck. Is there any problem with my WCF .. If I remove the Input param I can call the URL. I dunno what may be the issue. – Suave Nti Dec 18 '12 at 21:31
  • Well I think I found the issue. It is with the `{"data" : "24.3"}` .. for some reasons I need to enclose that as a string thats when my request is going through.. like this "{data : 24.3}" .. anyone care to explain reasons ? – Suave Nti Dec 18 '12 at 21:37
  • 2
    @GJSJ: then you are no longer sending a JSON object, but a JSON string. That'd be a server-side issue, nothing to do with Python or `requests`. – Martijn Pieters Dec 18 '12 at 21:54
  • @GJSJ: just try that out with `http://httpbin.org/post` as the URL; it responds with a JSON structure, including a `json` key to show you what JSON it received if you send JSON. – Martijn Pieters Dec 18 '12 at 22:03
  • agreed with your comment server is expecting a json string so I need to pass a json string rather than an object. Thanks Cheers !! – Suave Nti Dec 18 '12 at 22:18
  • @GJSJ: Often this means whoever coded the server put a `json.loads`/`JSON.decode`/equivalent in the script, even though the server container or framework has already done the decoding, so the service only works if the client "double-encodes" the data. (Which, as J.F. Sebastian points out, is actually not legal for `application/json`, but most servers won't care.) The right fix is to fix the server… but if it's not something you have control over, your workaround is basically equivalent to double-encoding, so it works just as well. – abarnert Dec 18 '12 at 22:30