Having trouble uploading a file via requests library to API - python

I'm trying to automate a manual process, which requires hitting an endpoint and uploading a file, in addition to providing other form data. I'm receiving a 400 Bad Request with my Python code.
Here is a curl request that works:
curl -X POST "https://xxxx/backfill" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "rsid=some_rsid" -F "file=#300038045-backfill.txt;type=text/plain" -H "Authorization: Basic xxx"
Here is the Python code:
def create_backfill(backfill_url: str, rsid: str, file_path: str, auth: Tuple[str, str]):
headers = {'Content-Type': 'multipart/form-data', 'Accept': 'application/json'}
form_data = {'rsid': rsid}
files = {'file': (os.path.basename(file_path), open(file_path, 'rb'), 'text/plain')}
response = requests.post(backfill_url, data=form_data, files=files, headers=headers, auth=auth)
response.raise_for_status()
return response.json()
Not sure exactly what I'm doing incorrectly
EDIT:
I ran both requests through https://httpbin.org, and got these results
Curl:
{
"args": {},
"data": "",
"files": {
"file": "data\n"
},
"form": {
"rsid": "some_rsid"
},
"headers": {
"Accept": "application/json",
"Authorization": "Basic xxx",
"Content-Length": "448",
"Content-Type": "multipart/form-data; boundary=------------------------929813e67050b28c",
"Host": "httpbin.org",
"User-Agent": "curl/7.54.0"
},
"json": null,
"origin": "192.150.9.200, 192.150.9.200",
"url": "https://httpbin.org/post"
}
Python:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "application/json",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Basic xxx",
"Content-Length": "32",
"Content-Type": "multipart/form-data",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "192.150.9.200, 192.150.9.200",
"url": "https://httpbin.org/

Related

return bearer token with requests

So i have a spotify account gen and was wondering if theres a way to return the bearer token to the accounts once they are made. I tried looking at the request reponse and headers but there was no bearer token.
def gen():
global accs
global invalid
currentuserfull = email()
username = name()
resp = requests.post("https://spclient.wg.spotify.com/signup/public/v1/account", data={
"birth_day": "1",
"birth_month": "01",
"birth_year": "1970",
"collect_personal_info": "undefined",
"creation_flow": "",
"creation_point": "https://www.spotify.com/uk/",
"displayname": username,
"username": username,
"gender": "neutral",
"iagree": "1",
"key": "a1e486e2729f46d6bb368d6b2bcda326",
"platform": "www",
"referrer": "https://www.spotify.com/uk/",
"send-email": "0",
"thirdpartyemail": "0",
"email": currentuserfull,
"password": password,
"password_repeat": password
}, headers={
"accept": "*/*",
"accept-language": "es-419,es;q=0.9",
"content-type": "application/x-www-form-urlencoded",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"sec-gpc": "1",
"referer": "https://www.spotify.com/",
"referrer-policy": "strict-origin-when-cross-origin"
})
How can i get the bearer token the accounts i generate or is there a way to see the headers of requests being sent with selenium?
is there a way to see the headers of requests being sent with selenium
It's possible with selenium-wire.
from seleniumwire import webdriver
driver = webdriver.Chrome()
driver.get('https://www.spotify.com')
# authorize and perform some actions
#then
for request in driver.requests:
print(request.headers['Authorization'])
some error handling might be required here if some request doesn't have Authorization header. I've not checked the exact behavior.
In this way you'll be able to view any request/response header.
Reference
https://pypi.org/project/selenium-wire/

HTTP 415 Unsupported Media Type client when sending data to server

Is it that my server is not supporting this request or I am sending some wrong data which doesn't seem to be the case.
# importing the requests library
import requests
# defining the api-endpoint
API_ENDPOINT = "http://10.176.14.170:5000/api/du/v1"
# data to be sent to api
data = {"name":"du1"}
# sending post request and saving response as response object
r = requests.post(url = API_ENDPOINT, data = data)
print(r)
Response:
<Response [415]>
Success from a web CLI that is running on same server.
post /api/du/v1
POST api/du/v1 {"name":"du1"}
I also get success when I send the POST request using curl command from same server
While the original curl command was not included, though it was stated that the data was sent via POST to the server as is. I am going to assume that the curl -d '{"name":"du1"}' was used along with the appropriate headers being sent. Now to demonstrate this, the following command is used:
$ curl http://httpbin.org/post -d '{"name":"du1"}' -H 'Content-Type: application/json'
{
"args": {},
"data": "{\"name\": \"du1\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "15",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/7.61.0"
},
"json": {
"name": "du1"
},
"url": "http://httpbin.org/post"
}
Note the bolded data key - that's what likely was being read by your application. On the contrary, doing the same thing as done with the provided usage of requests at the same endpoint:
>>> import requests
>>> r = requests.post('http://httpbin.org/post', data={"name":"du1"})
>>> print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "du1"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "8",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.21.0"
},
"json": null,
"url": "http://httpbin.org/post"
}
Note how the requests had been decoded into a form simply because a) the data submitted was a raw dictionary, and not a JSON encoded string and b) the header was not supplied. To fix this, simply encode the data and supply the appropriate headers:
>>> import requests
>>> import json
>>> data = json.dumps({"name":"du1"})
>>> r = requests.post(
... 'http://httpbin.org/post', data=data,
... headers={'Content-Type': 'application/json'})
>>> print(r.text)
{
"args": {},
"data": "{\"name\": \"du1\"}",
"files": {},
"form": {},
...
"json": {
"name": "du1"
},
...
}
Note that the server is now able to interpret the provided data, and can decode that as JSON.

Send body of POST request

I inspected how a request was sent to a website in firefox:
(Unfortunately I had to change the website URL to a fake one to prevent the server form being requested too much).
I tried to do this request in python:
import requests
import json
seq = 'ATGGCAGACTCTATTGAGGTC'
url = 'http://www.test.com'
body = {'QUERY': seq}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post(url, data=json.dumps(body), headers=headers)
print(r.text)
However when doing this the website says: Empty gene sequence passed for blast analysis. Please enter a valid gene sequence. So that means that the sequence (i.e. QUERY) is not sent correctly to the server. What am I missing here?
(P.s. hopefully missing of the website is not a problem to answer this question, if it is please let me know maybe I can ask to mention their website)
I am guessing the string / sequence that you are submitting to that particular website is the problem. I ran your sample code against a POST accepting website:
import requests
import json
seq = 'ATGGCAGACTCTATTGAGGTC'
url = 'http://httpbin.org/post'
body = {'QUERY': seq}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post(url, data=json.dumps(body), headers=headers)
print(r.text)
And got this result, which shows your query properly formed:
{
"args": {},
"data": "{\"QUERY\": \"ATGGCAGACTCTATTGAGGTC\"}",
"files": {},
"form": {},
"headers": {
"Accept": "text/plain",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "34",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": {
"QUERY": "ATGGCAGACTCTATTGAGGTC"
},
"origin": "2.122.222.8, 2.122.222.8",
"url": "https://httpbin.org/post"
}
Are you sure the it's supposed to be "QUERY=<>?" Cause it could be incorrect formatting of the body. Normally it's in JSON format as in "title: information." Note the ':' rather than the '='

Include multiple headers in python requests

I have this HTTPS call in curl below;
header1="projectName: zhikovapp"
header2="Authorization: Bearer HZCdsf="
bl_url="https://BlazerNpymh.com/api/documents?pdfDate=$today"
curl -s -k -H "$header1" -H "$header2" "$bl_url"
I would like to write an equivalent python call using requests module.
header ={
"projectName": "zhikovapp",
"Authorization": "Bearer HZCdsf="
}
response = requests.get(bl_url, headers = header)
However, the request was not valid. What is wrong?
The contents of the returned response is like this;
<Response [400]>
_content = '{"Message":"The request is invalid."}'
headers = {'Content-Length': '37', 'Access-Control-Allow-Headers': 'projectname, authorization, Content-Type', 'Expires': '-1', 'cacheControlHeader': 'max-age=604800', 'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Date': 'Sat, 15 Oct 2016 02:41:13 GMT', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Content-Type': 'application/json; charset=utf-8'}
reason = 'Bad Request'
I am using python 2.7
EDIT: I corrected some syntex errors after Soviut pointed them out.
In request.get() the headers argument should be defined as a dictionary, a set of key/value pairs. You've defined a set (a unique list) of strings instead.
You should declare your headers like this:
headers = {
"projectName": "zhikovapp",
"Authorization": "Bearer HZCdsf="
}
response = requests.get(bl_url, headers=headers)
Note the "key": "value" format of each line inside the dictionary.
Edit: Your Access-Control-Allow-Headers say they'll accept projectname and authorization in lower case. You've named your header projectName and Authorization with upper case letters in them. If they don't match, they'll be rejected.
If you have $today defined in the shell you make curl call from, and you don't substitute it in the requests' call URL, then it's a likely reason for the 400 Bad Request.
Access-Control-* and other CORS headers have nothing to do with non-browser clients. Also HTTP headers are generally case insensitive.
Following #furas's advice here's the output:
$ curl -H "projectName: zhikovapp" -H "Authorization: Bearer HZCdsf=" \
http://httpbin.org/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Authorization": "Bearer HZCdsf=",
"Host": "httpbin.org",
"Projectname": "zhikovapp",
"User-Agent": "curl/7.35.0"
},
"origin": "1.2.3.4",
"url": "http://httpbin.org/get"
}
And the same request with requests:
import requests
res = requests.get('http://httpbin.org/get', headers={
"projectName" : "zhikovapp",
"Authorization" : "Bearer HZCdsf="
})
print(res.json())
{
'args': {},
'headers': {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, compress',
'Authorization': 'Bearer HZCdsf=',
'Host': 'httpbin.org',
'Projectname': 'zhikovapp',
'User-Agent': 'python-requests/2.2.1 CPython/3.4.3 '
'Linux/3.16.0-38-generic'
},
'origin': '1.2.3.4',
'url': 'http://httpbin.org/get'
}
As you can see the only difference is User-Agent header. It's unlikely the cause but you can easily set it in headers to the value you like.

curl vs python "requests" when hitting APIs

I am trying to hit the Bitbucket API for my account, and a successful attempt looks like:
curl --user screename:mypassword https://api.bitbucket.org/1.0/user/repositories
in the command line. In python, I try:
import requests
url = 'https://api.bitbucket.org/1.0/user/repositories'
then
r = requests.post(url, data={'username': myscreename, 'password':mypassword})
and
r = requests.post(url, data="myscreename:mypassword")
and
r = requests.post(url, data={"user": "myscreename:mypassword"})
all get 405 error. The API is https://confluence.atlassian.com/bitbucket/rest-apis-222724129.html.
I wonder:
What am I doing wrong in the requests version, they all look similar to my curl attempt
What is the difference between requesting with curl and python requests module? What general pattern can I recognize when reading an API with a curl example and then writing it in python?
Thank you
ANSWER:
it required the right headers
https://answers.atlassian.com/questions/18451025/answers/18451117?flashId=-982194107
UPDATE:
# ===============
# get user
# ===============
import requests
import json
# [BITBUCKET-BASE-URL], i.e.: https://bitbucket.org/
url = '[BITBUCKET-BASE-URL]/api/1.0/user/'
headers = {'Content-Type': 'application/json'}
# get user
# [USERNAME], i.e.: myuser
# [PASSWORD], i.e.: itspassword
r = requests.get(url, auth=('[USERNAME]', '[PASSWORD]'), headers=headers)
print(r.status_code)
print(r.text)
#print(r.content)
Here's a way to do basic HTTP auth with Python's requests module:
requests.post('https://api.bitbucket.org/1.0/user/repositories', auth=('user', 'pass'))
With the other way you're passing the user/pass through the request's payload, which is not desired since HTTP basic auth has its own place in the HTTP protocol.
If you want to "see" what's happening under the hood with your request I recommend using httpbin:
>>> url = "http://httpbin.org/post"
>>> r = requests.post(url, data="myscreename:mypassword")
>>> print r.text
{
"args": {},
"data": "myscreename:mypassword",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "22",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.5.1 CPython/2.7.6 Darwin/14.3.0"
},
"json": null,
"origin": "16.7.5.3",
"url": "http://httpbin.org/post"
}
>>> r = requests.post(url, auth=("myscreename", "mypassword"))
>>> print r.text
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Basic bXlzY3JlZW5hbWU6bXlwYXNzd29yZA==",
"Content-Length": "0",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.5.1 CPython/2.7.6 Darwin/14.3.0"
},
"json": null,
"origin": "16.7.5.3",
"url": "http://httpbin.org/post"
}
And with curl:
curl -X POST --user myscreename:mypassword http://httpbin.org/post
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Authorization": "Basic bXlzY3JlZW5hbWU6bXlwYXNzd29yZA==",
"Host": "httpbin.org",
"User-Agent": "curl/7.37.1"
},
"json": null,
"origin": "16.7.5.3",
"url": "http://httpbin.org/post"
}
Notice the resemblance between the last python example and the cURL one.
Now, getting right the API's format is another story, check out this link: https://answers.atlassian.com/questions/94245/can-i-create-a-bitbucket-repository-using-rest-api
The python way should be something like this:
requests.post('https://api.bitbucket.org/1.0/repositories', auth=('user', 'pass'), data = "name=repo_name")
With python3, you can use json={...} instead of data={...}, and it will set the header automatically to application/json:
import requests
url = 'https://api.bitbucket.org/1.0/user/repositories'
data = {
'data1': 'asd',
'data2': 'asd'
}
req = requests.post(url, auth=('user', 'password'), json = data)
data = req.json()
# data['index']

Categories