I need to POST a JSON from a client to a server. I'm using Python 2.7.1 and simplejson. The client is using Requests. The server is CherryPy. I can GET a hard-coded JSON from the server (code not shown), but when I try to POST a JSON to the server, I get "400 Bad Request".
Here is my client code:
data = {'sender': 'Alice',
'receiver': 'Bob',
'message': 'We did it!'}
data_json = simplejson.dumps(data)
payload = {'json_payload': data_json}
r = requests.post("http://localhost:8080", data=payload)
Here is the server code.
class Root(object):
def __init__(self, content):
self.content = content
print self.content # this works
exposed = True
def GET(self):
cherrypy.response.headers['Content-Type'] = 'application/json'
return simplejson.dumps(self.content)
def POST(self):
self.content = simplejson.loads(cherrypy.request.body.read())
Any ideas?
__init__
methods with a content
argument (and does not claim to in the link you supply). In the detailed example they have, the user supplies the code that calls __init__
and provides the arguments, which we have not seen here so I have no idea what state your object is in when your # this works
comment is relevant.
requests
package. Postman documentation
Starting with Requests version 2.4.2, you can use the json=
parameter (which takes a dictionary) instead of data=
(which takes a string) in the call:
>>> import requests
>>> r = requests.post('http://httpbin.org/post', json={"key": "value"})
>>> r.status_code
200
>>> r.json()
{'args': {},
'data': '{"key": "value"}',
'files': {},
'form': {},
'headers': {'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '16',
'Content-Type': 'application/json',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.4.3 CPython/3.4.0',
'X-Request-Id': 'xx-xx-xx'},
'json': {'key': 'value'},
'origin': 'x.x.x.x',
'url': 'http://httpbin.org/post'}
It turns out I was missing the header information. The following works:
import requests
url = "http://localhost:8080"
data = {'sender': 'Alice', 'receiver': 'Bob', 'message': 'We did it!'}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post(url, data=json.dumps(data), headers=headers)
application/json
in GET
and somehow missed that you hadn't provided it on the request. You may also need to make sure that you return something from POST
or you might get a 500
.
r
, I get <Response [200]>
.
json.dumps
here. The data
parameter of requests
works fine with dictionaries. No need for converting to a string.
From requests 2.4.2 (https://pypi.python.org/pypi/requests), the "json" parameter is supported. No need to specify "Content-Type". So the shorter version:
requests.post('http://httpbin.org/post', json={'test': 'cheers'})
Which parameter between data
/ json
/ files
you need to use depends on a request header named Content-Type
(you can check this through the developer tools of your browser).
When the Content-Type
is application/x-www-form-urlencoded
, use data=
:
requests.post(url, data=json_obj)
When the Content-Type
is application/json
, you can either just use json=
or use data=
and set the Content-Type
yourself:
requests.post(url, json=json_obj)
requests.post(url, data=jsonstr, headers={"Content-Type":"application/json"})
When the Content-Type
is multipart/form-data
, it's used to upload files, so use files=
:
requests.post(url, files=xxxx)
curlify
to see the request made from the response, the content type header will not be set unless you follow these instructions. print(curlify.to_curl(project.request))
Content-Type
header. Consequently, data=json_obj
is basically always incorrect; the value must be stringified first, either explicitly by the caller or implicitly via the json
parameter.
The better way is:
url = "http://xxx.xxxx.xx"
data = {
"cardno": "6248889874650987",
"systemIdentify": "s08",
"sourceChannel": 12
}
resp = requests.post(url, json=data)
headers = {"charset": "utf-8", "Content-Type": "application/json"}
url = 'http://localhost:PORT_NUM/FILE.php'
r = requests.post(url, json=YOUR_JSON_DATA, headers=headers)
print(r.text)
Works perfectly with python 3.5+
client:
import requests
data = {'sender': 'Alice',
'receiver': 'Bob',
'message': 'We did it!'}
r = requests.post("http://localhost:8080", json={'json_payload': data})
server:
class Root(object):
def __init__(self, content):
self.content = content
print self.content # this works
exposed = True
def GET(self):
cherrypy.response.headers['Content-Type'] = 'application/json'
return simplejson.dumps(self.content)
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def POST(self):
self.content = cherrypy.request.json
return {'status': 'success', 'message': 'updated'}
json
arg automatically adds the content-type
header for POST
in newer versions of Python.
I solved it this way:
from flask import Flask, request
from flask_restful import Resource, Api
req = request.json
if not req :
req = request.form
req['value']
With current requests
you can pass in any data structure that dumps to valid JSON , with the json
parameter, not just dictionaries (as falsely claimed by the answer by Zeyang Lin).
import requests
r = requests.post('http://httpbin.org/post', json=[1, 2, {"a": 3}])
this is particularly useful if you need to order elements in the response.
It always recommended that we need to have the ability to read the JSON file and parse an object as a request body. We are not going to parse the raw data in the request so the following method will help you to resolve it.
def POST_request():
with open("FILE PATH", "r") as data:
JSON_Body = data.read()
response = requests.post(url="URL", data=JSON_Body)
assert response.status_code == 200
Success story sharing
json.dumps(your_json)
and converts the result from astr
tobytes
.auth=HTTPBasicAuth(user, pass)
to the params.