Can't post file using python requests - Translation from curl - python

The following command works, but I can't reproduce it using python-requests (2.18.4) :
curl -X POST -H "Authorization: Bearer ..." \
-H "Content-Type: multipart/form-data" \
-F 'metadata={...}
-F 'data=#data.bz2;type=application/octet-stream'
https://www....com
Using the send_devices below, I receive "Unsupported Media Type""
def send_devices(basic_auth):
endpoint_api = ' https://www....com'
with open('data.bz2','rb') as payload:
response = requests.post(endpoint_api, data={'metadata': ...,
'data': payload},
headers={'Authorization': 'Bearer {0}'.format(basic_auth})
After some comments, I also tried and the error now is "Invalid Metadata Json String":
def send_devices(basic_auth):
endpoint_api = ' https://www....com'
files = {'file': ('data.bz2', open('data.bz2', 'rb'), 'application/octet-stream')}
response = requests.post(endpoint_api, data={"metadata": {"extensions":{"urnType":"IDFA"}}},
files=files, headers={'Authorization': 'Bearer {0}'.format(basic_auth)})

In the first example, was missing the file type
'data': ('data.bz2', open('data.bz2', 'rb'), 'application/octet-stream'),
In the second example, is necessary to add extra post data on the same files dict. Even if is not a dict:
'metadata': ('metadata.csv', json.dumps({"extensions": ...}))}
The solution:
def send_devices(basic_auth):
endpoint_api = ' https://www....com'
files = {'data': ('data.bz2', open('data.bz2', 'rb'), 'application/octet-stream'),
'metadata': ('metadata.csv', json.dumps({"extensions": ...}))}
response = requests.post(endpoint_api, files=files,
headers={'Authorization': 'Bearer {0}'.format(basic_auth)})

Make sure the payload is formatted correctly. It appears you are missing an '{' in your second object within your payload.
The HTTP 415 Unsupported Media Type client error response code indicates that the server refuses to accept the request because the payload format is in an unsupported format.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415

You should put it as file (not as data).
r = requests.post(url, files={'file': open('data.bz2', 'rb')})
works fine.

Related

translating simple curl call to python/django request

I'm attempting to translate the following curl request to something that will run in django.
curl -X POST https://api.lemlist.com/api/hooks --data '{"targetUrl":"https://example.com/lemlist-hook"}' --header "Content-Type: application/json" --user ":1234567980abcedf"
I've run this in git bash and it returns the expected response.
What I have in my django project is the following:
apikey = '1234567980abcedf'
hookurl = 'https://example.com/lemlist-hook'
data = '{"targetUrl":hookurl}'
headers = {'Content-Type': 'application/json'}
response = requests.post(f'https://api.lemlist.com/api/hooks/', data=data, headers=headers, auth=('', apikey))
Running this python code returns this as a json response
{}
Any thoughts on where there might be a problem in my code?
Thanks!
Adding to what nikoola said, I think you want that whole data line to be as follows so you aren't passing that whole thing as a string. Requests will handle serializing and converting python objects to json for you [EDIT: if you use the json argument instead of data].
source: https://requests.readthedocs.io/en/master/user/quickstart/#more-complicated-post-requests
Instead of encoding the dict yourself, you can also pass it directly
using the json parameter (added in version 2.4.2) and it will be
encoded automatically:
Note, the json parameter is ignored if either data or files is passed.
Using the json parameter in the request will change the Content-Type
in the header to application/json.
data = {"targetUrl":hookurl}
import requests
headers = {
'Content-Type': 'application/json',
}
data = '{"targetUrl":"https://example.com/lemlist-hook"}'
response = requests.post('https://api.lemlist.com/api/hooks', headers=headers, data=data, auth=('', '1234567980abcedf'))
You can visit this url:-
https://curl.trillworks.com/

How to pass a list to data parameter in requests.patch?

I'm trying to add a role to a user in Auth0 via requests in python, but I'm having problems with the data part. I've tried to send this data passing a pure list, using json parameter instead of data and other options...
requests.patch(
url='my_url',
data=json.dumps(["my_role_id"]),
headers={'Authorization': 'Bearer my_token',
'Content-Type': 'application/json'}
)
Following the docs, if I try to send the same data via cURL or in a client like Insomnia, it works.
curl --request PATCH \
--url 'https://{extension_url}/users/{user_id}/roles' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/json' \
--data '[ "{role_id}" ]'
The data content passed to service in the python sample is not the same as in the cURL sample. Try this (postman-echo helps to figure out what is going on):
import requests
import json
role_ids = ["my-role-id"]
role_id_array = [ "{%s}" % x for x in role_ids ]
resp = requests.patch(
url='http://postman-echo.com/patch',
data=json.dumps(role_id_array),
headers={'Authorization': 'Bearer my_token',
'Content-Type': 'application/json'}
)
print(resp.content)
I solved it! My url was wrong with the user_id incomplete and Auth0's authorization API didn't tell me that.

Problems with POST to Quire API

I have been playing with Quire API using python and while the GET calls work fine, I can't do any successful POST calls. I get 400 error: Bad Request. I would appreciate any hints on what I might be doing wrong.
Below are relevant code snippets:
AUTH_ENDPOINT = 'https://quire.io/oauth/token'
API_ENDPOINT = 'https://quire.io/api'
data = {
'grant_type' : 'refresh_token',
'refresh_token' : 'my_refresh_code',
'client_id' : 'my_client_id',
'client_secret' : 'my_client_secret'
}
r = requests.post(url=AUTH_ENDPOINT, data=data)
response = json.loads(r.text)
access_token = response['access_token']
headers = {'Authorization' : 'Bearer {token}'.format(token=access_token)}
# This works fine
r = requests.get(url=API_ENDPOINT + '/user/id/me', headers=headers)
user = json.loads(r.text)
print(user)
# This doesn't work
task_oid = 'my_task_oid'
data = {
'description' : 'Test Comment'
}
r = requests.post(
url=API_ENDPOINT + '/comment/' + task_oid,
data=data,
headers=headers,
)
I am not familiar with the python requests API, so I don't know about default headers.
However it looks like you missed to send the request data as a JSON string:
here what worked for me from java script:
uri: '/comment/my_task_oid',
method: 'POST',
body: '{"description":"hello comment"}'
maybe it helps, in python as well.
also a curl example:
curl -X POST -H 'Authorization: Bearer my_access_token' -d "{\"description\" : \"a test comment\"}" https://quire.io/api/comment/my_task_oid
The answer provided by #cor3000 hinted that post data should be passed as JSON. I tested it out and indeed it works. Here is required modification to the POST reqest:
r = requests.post(
url=API_ENDPOINT + '/comment/' + task_oid,
data=json.dumps(data),
headers=headers,
)
Alternatively you can also do:
r = requests.post(
url=API_ENDPOINT + '/comment/' + task_oid,
json=data,
headers=headers,
)
More details in requests documentation: https://requests.kennethreitz.org/en/master/user/quickstart/#more-complicated-post-requests

Issues with JSON Data in Python

I am working on a project to incorporate Verizon Thingspace REST APIs into a Python program.
A co-worker provided me with an example in CURL that works (I am not familiar with Curl so am trying to convert into Python).
curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" --header "VZ-M2M-Token: 621d9779-f8bc-4fe9-91dd-b726c52e7117" --header "Authorization: Bearer 89ba225e1438e95bd05c3cc288d3591" -d "{\"accountName\": \"TestAccount-1\"}" https://thingspace.verizon.com/api/m2m/v1/devices/actions/list
I'm trying to convert this exact same request into a Python function. Here's what I have:
import requests
def getList(token):
url = "https://thingspace.verizon.com/api/m2m/v1/devices/actions/list"
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer 89ba225e1438e95bd05c3cc288d3591",
"VZ-M2M-Token": "f7ef3a35-abb6-418b-92d4-7cdac8b06c5f",
}
data = {"accountName": "TestAccount-1"}
print data
deviceList = requests.post(url, data=data, headers=headers)
print headers
print (deviceList.status_code, deviceList.reason, deviceList.text)
return deviceList
When I run this, I get the following error message back in JSON:
(400, 'Bad Request',
u'{"errorCode":"REQUEST_FAILED.UnexpectedError","errorMessage":"Could
not read document: Unrecognized token \'accountName\': was expecting
(\'true\', \'false\' or \'null\')\n at [Source:
java.io.PushbackInputStream#51ceaf1d; line: 1, column: 13]; nested
exception is com.fasterxml.jackson.core.JsonParseException:
Unrecognized token \'accountName\': was expecting (\'true\', \'false\'
or \'null\')\n at [Source: java.io.PushbackInputStream#51ceaf1d;
line: 1, column: 13]"}')
You can reference the API information here: https://thingspace.verizon.com/developer/apis#/Connectivity%20Management/API%20Reference/Retrieve%20Device%20Information.html
I believe there might be something wrong with my JSON string but I need another set of eyes.
data is not automatically converted to json, you have to do this explicitly:
deviceList = requests.post(url, data=json.dumps(data), headers=headers)

unable to post file+data using python-requests

I'm able to post file using curl
curl -X POST -i -F name='barca' -F country='spain' -F
file=#/home/messi/Desktop/barca.png 'http://localhost:8080/new_org/hel/concerts'
Which I can get (file) as
curl -X GET -H 'Accept: image/png' 'http://localhost:8080/new_org/hel/concerts/<id or name of entity>'
But when I tried same thing using requests.post, I got error. Does anybody know why this happen. (Post Error encounter when file pointer is not at last, but when file pointer is at last, I got response 200 but file is not posted)
import requests
url = 'http://localhost:8080/new_org/hel/concerts'
file = dict(file=open('/home/messi/Desktop/barca.png', 'rb'))
data = dict(name='barca', country='spain')
response = requests.post(url, files=file, data=data)
Error: (from usergrid) with response code: 400
{u'duration': 0,
u'error': u'illegal_argument',
u'error_description': u'value is null',
u'exception': u'java.lang.IllegalArgumentException',
u'timestamp': 1448330119021}
https://github.com/apache/usergrid
I believe the problem is that Python is not sending a content-type field for the image that you are sending. I traced through the Usergrid code using a debugger and saw that curl is sending the the content-type for the image and Python is not.
I was able to get this exact code to work on my local Usergrid:
import requests
url = 'http://10.1.1.161:8080/test-organization/test-app/photos/'
files = { 'file': ('13.jpg', open('/Users/dave/Downloads/13.jpg', 'rb'), 'image/jpeg')}
data = dict(name='barca', country='spain')
response = requests.post(url, files=files, data=data)
It is possible that Waken Meng's answer did not work because of the syntax of the files variable, but I'm no Python expert.
I met a problem before when i try to upload image files. Then I read the doc and do this part:
You can set the filename, content_type and headers explicitly:
Here is how I define the file_data:
file_data = [('pic', ('test.png', open('test.png'), 'image/png'))]
r = requests.post(url, files=file_data)
file_data should be a a list: [(param_name, (file_name, file, content_type))]
This works for me, hope can help you.

Categories