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?
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)
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)
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'}
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.
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']
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
Related
I am requesting to mindbodyapi to get token with the following code using requests library
def get_staff_token(request):
URL = "https://api.mindbodyonline.com/public/v6/usertoken/issue"
payload = {
'Api-Key': API_KEY,
'SiteId': "1111111",
'Username': 'user#xyz.com',
'Password': 'xxxxxxxx',
}
r = requests.post(url=URL, params=payload)
print(r.text)
return HttpResponse('Done')
gives a response as follows
{"Error":{"Message":"Missing API key","Code":"DeniedAccess"}}
But if I request the following way it works, anybody could tell me, what I am doing wrong on the above code.
conn = http.client.HTTPSConnection("api.mindbodyonline.com")
payload = "{\r\n\t\"Username\": \"username\",\r\n\t\"Password\": \"xxxxx\"\r\n}"
headers = {
'Content-Type': "application/json",
'Api-Key': API_KEY,
'SiteId': site_id,
}
conn.request("POST", "/public/v6/usertoken/issue", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
In the second one, you are passing the API Key in headers and the credentials in the body of the request. In the first, you are sending both the API Key and credentials together in the query string, not the request body. Refer to requests.request() docs
Just use two dictionaries like in your second code and the correct keywords, I think it should work:
def get_staff_token(request):
URL = "https://api.mindbodyonline.com/public/v6/usertoken/issue"
payload = {
'Username': 'user#xyz.com',
'Password': 'xxxxxxxx',
}
headers = {
'Content-Type': "application/json",
'Api-Key': API_KEY,
'SiteId': "1111111",
}
r = requests.post(url=URL, data=payload, headers=headers)
print(r.text)
return HttpResponse('Done')
I'm trying to upload PDF-file in the Xero account using the python request library (POST method) and Xeros FilesAPI said "Requests must be formatted as multipart MIME" and have some required fields (link) but I don't how to do that exactly...If I do GET-request I'm getting the list of files in the Xero account but having a problem while posting the file (POST-request)...
My Code:
post_url = 'https://api.xero.com/files.xro/1.0/Files/'
files = {'file': open('/home/mobin/PycharmProjects/s3toxero/data/in_test_upload.pdf', 'rb')}
response = requests.post(
post_url,
headers={
'Authorization': 'Bearer ' + new_tokens[0],
'Xero-tenant-id': xero_tenant_id,
'Accept': 'application/json',
'Content-type': 'multipart/form-data; boundary=JLQPFBPUP0',
'Content-Length': '1068',
},
files=files,
)
json_response = response.json()
print(f'Uploading Responsoe ==> {json_response}')
print(f'Uploading Responsoe ==> {response}')
Error Mesage/Response:
Uploading Responsoe ==> [{'type': 'Validation', 'title': 'Validation failure', 'detail': 'No file is was attached'}]
Uploading Responsoe ==> <Response [400]>
As I see you're improperly set the boundary. You set it in the headers but not tell to requests library to use custom boundary. Let me show you an example:
>>> import requests
>>> post_url = 'https://api.xero.com/files.xro/1.0/Files/'
>>> files = {'file': open('/tmp/test.txt', 'rb')}
>>> headers = {
... 'Authorization': 'Bearer secret',
... 'Xero-tenant-id': '42',
... 'Accept': 'application/json',
... 'Content-type': 'multipart/form-data; boundary=JLQPFBPUP0',
... 'Content-Length': '1068',
... }
>>> print(requests.Request('POST', post_url, files=files, headers=headers).prepare().body.decode('utf8'))
--f3e21ca5e554dd96430f07bb7a0d0e77
Content-Disposition: form-data; name="file"; filename="test.txt"
--f3e21ca5e554dd96430f07bb7a0d0e77--
As you can see the real boundary (f3e21ca5e554dd96430f07bb7a0d0e77) is different from what was passed in the header (JLQPFBPUP0).
You can actually directly use the requests module to controll boundary like this:
Let's prepare a test file:
$ touch /tmp/test.txt
$ echo 'Hello, World!' > /tmp/test.txt
Test it:
>>> import requests
>>> post_url = 'https://api.xero.com/files.xro/1.0/Files/'
>>> files = {'file': open('/tmp/test.txt', 'rb')}
>>> headers = {
... 'Authorization': 'Bearer secret',
... 'Xero-tenant-id': '42',
... 'Accept': 'application/json',
... 'Content-Length': '1068',
... }
>>> body, content_type = requests.models.RequestEncodingMixin._encode_files(files, {})
>>> headers['Content-type'] = content_type
>>> print(requests.Request('POST', post_url, data=body, headers=headers).prepare().body.decode('utf8'))
--db57d23ff5dee7dc8dbab418e4bcb6dc
Content-Disposition: form-data; name="file"; filename="test.txt"
Hello, World!
--db57d23ff5dee7dc8dbab418e4bcb6dc--
>>> headers['Content-type']
'multipart/form-data; boundary=db57d23ff5dee7dc8dbab418e4bcb6dc'
Here boundary is the same as in the header.
Another alternative is using requests-toolbelt; below example taken from this GitHub issue thread:
from requests_toolbelt import MultipartEncoder
fields = {
# your multipart form fields
}
m = MultipartEncoder(fields, boundary='my_super_custom_header')
r = requests.post(url, headers={'Content-Type': m.content_type}, data=m.to_string())
But it is better not to pass bundary by hand at all and entrust this work to the requests library.
Update:
A minimal working example using Xero Files API and Python request:
from os.path import abspath
import requests
access_token = 'secret'
tenant_id = 'secret'
filename = abspath('./example.png')
post_url = 'https://api.xero.com/files.xro/1.0/Files'
files = {'filename': open(filename, 'rb')}
values = {'name': 'Xero'}
headers = {
'Authorization': f'Bearer {access_token}',
'Xero-tenant-id': f'{tenant_id}',
'Accept': 'application/json',
}
response = requests.post(
post_url,
headers=headers,
files=files,
data=values
)
assert response.status_code == 201
I've tested this with Xero's Files API to upload a file called "helloworld.rtf" in the same directory as my main app.py file.
var1 = "Bearer "
var2 = YOUR_ACCESS_TOKEN
access_token_header = var1 + var2
body = open('helloworld.rtf', 'rb')
mp_encoder = MultipartEncoder(
fields={
'helloworld.rtf': ('helloworld.rtf', body),
}
)
r = requests.post(
'https://api.xero.com/files.xro/1.0/Files',
data=mp_encoder, # The MultipartEncoder is posted as data
# The MultipartEncoder provides the content-type header with the boundary:
headers={
'Content-Type': mp_encoder.content_type,
'xero-tenant-id': YOUR_XERO_TENANT_ID,
'Authorization': access_token_header
}
)
looks like you got it solved. For reference and any future developers who are using the Xero supported package (https://github.com/XeroAPI/xero-python)
We just added the files_api example code to the sample app so the following would upload a file if you were using the Python SDK
https://github.com/XeroAPI/xero-python-oauth2-app/pull/29/files
name = "my-image"
filename= "my-image.jpg"
mime_type = "image/jpg"
with open('my-image.jpg', 'rb') as f:
body = f.read()
try:
file_object = files_api.upload_file(
xero_tenant_id,
name = name,
filename= filename,
mime_type = mime_type,
body=body
)
except AccountingBadRequestException as exception:
json = jsonify(exception.error_data)
else:
json = serialize_model(file_object)
I'm trying to retrieve only requestId object from json results after querying my API.
my code:
def findRequests(group, token, user):
headers = { 'accept': 'application/json',
'Content-Type': 'application/json',
'token': token,
'user': user}
endpoint = 'requests/find'
body = {'groupIDs': [group], "createdDate": {'operator': "BETWEEN", 'fromDate': "01-APR-2020 00:00:00", 'toDate': "21-APR-2020 00:00:00"}}
r = requests.post(url = host + endpoint, headers = headers, json=body, verify=False)
data = json.loads(r.text)
print (data[0]['requestId'])
json data:
[{'requestId': 2567887, 'requestName': 'Here is a sample name', 'requestSubject': 'sample subject', 'createdDate': '01-APR-2020 14:06:03'}, {'requestId': 7665432,...}]
then I would like to save all the values for requestId object found in results as a list:
myRequestsList = print(findRequests(group, token, user))
However the above will only return a single requestId and not all of the ones that were returned in data variable in def findRequests(group, token, user). What am I doing wrong?
Output:
2567887
None
Desired output:
2567887,
7665432
Could someone help me with this? thanks in advance!
First, you should modify your func:
Then, assign the variable to the func, not the print:
myRequestsList = list(findRequests(group, token, user)))
(!) However, I assume that group,token, user are replaced by other variables.
And finally, to get the output:
for req in myRequestsList:
print(req)
Later edit:
def findRequests(group, token, user):
headers = { 'accept': 'application/json',
'Content-Type': 'application/json',
'token': token,
'user': user}
endpoint = 'requests/find'
body = {'groupIDs': [group], "createdDate": {'operator': "BETWEEN", 'fromDate': "01-APR-2020 00:00:00", 'toDate': "21-APR-2020 00:00:00"}}
r = requests.post(url = host + endpoint, headers = headers, json=body, verify=False)
data = json.loads(r.text)
final_list = []
for each_req in data:
final_list.append(each_req['requestId'])
return final_list
myRequestsList = findRequests(group, token, user)
for each in myRequestsList:
print(each)
I need your help.
I am looking to communicate with a REST API containing sensor data at port number 3.
I have a Json (POST) request that works perfectly executed on a REST client like Insomnia.
My request :
{ "header": { "portNumber": 3 }, "data": { "index": 40 } }
Picture of my request
However I am unable to make it work on Python and to recover data from my sensor.
My Python code :
import requests
import json
url = 'http://192.168.1.100/iolink/sickv1' # Address of the OctoPrint Server
header = {'portNumber': '3', 'Content-Type': 'application/json'} #Basic request's header
data = {'index': 40}
def get_sensor_measure():
r = requests.post(url + '/readPort', headers=header, data=data)
print(r.content)
print(r.status_code)
I get the error:
b'{"header":{"status":1,"message":"Parsing Failed"}}'
Thank you in advance
You should pass 'portNumber': '3' in data not in header:
header = {'Content-Type': 'application/json'}
data = {'header': {'portNumber': '3'}, 'data': {'index': 40}}
And also as Karl stated in his answer you need to changes data to json:
r = requests.post(url + '/readPort', headers=header, json=data)
My guess would be that you are using the wrong field to pass your payload. It isn't really obvious, but the requests package expects JSON-type payload to be sent with the json field, not the data field, i.e.:
r = requests.post(url + '/readPort', headers=header, json=data)
With a few changes (Bold), it works. Thanks
url = 'http://192.168.1.100/iolink/sickv1' # Address of the OctoPrint Server
header = {'Content-Type': 'application/json'} #Basic request's header
**data = {'header': {'portNumber': 3}, 'data': {'index': 40}}**
def get_sensor_measure():
r = requests.post(url + '/readPort', headers=header, json=data)
print(r.content)
print(r.status_code)
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.
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)