Python Post - keep getting response 400, but curl works - python

I have a simple python 3 script that send a post request to delete a project in SonarQube. While I am keep getting in my python script, a simple curl commands works... any idea what is wrong with my python script?
import requests
headers = {
'Authorization': 'Basic YWRtaW46YWRtaW4=',
}
files = [
('key', 'com.eclipseoptions.viewserver:viewserver:feature_VS-313-add-an-instruction-event-and-view'),
]
r = requests.post('http://devsonar/api/projects/delete', headers=headers, files=files)
print(r)
The following curl command works fine:
curl -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" -F "key=com.eclipseoptions.viewserver:viewserver:feature_VS-313-add-an-instruction-event-and-view" "http://devsonar/api/projects/delete"

Python requests is really a good library. Files option in post is used to upload file and I don't think com.eclipseoptions.viewserver:viewserver:feature_VS-313-add-an-instruction-event-and-view is a file if so, you have to read the file in binary mode and then send it like files = {'key': open(filename, 'rb')}. so the code should be:
import requests
files = {'key': open(filename, 'rb')}
headers = {'Authorization': 'Basic YWRtaW46YWRtaW4='}
response=requests.post(url,files=files)
check this for details on uploading files using requests library in python.
If it is not a file you can send the payload directly as a dictionary like this:
import requests
headers = {'Authorization': 'Basic YWRtaW46YWRtaW4='}
data = {'key': 'com.eclipseoptions.viewserver:viewserver:feature_VS-313-add-an-instruction-event-and-view'}
response=requests.post(url,data=data,headers=headers)
check this for details on sending payload.

You should have used data, not files, as an input to the python script, this should work:
import requests
headers = {
'Authorization': 'Basic YWRtaW46YWRtaW4=',
}
files = [
('key', 'com.eclipseoptions.viewserver:viewserver:feature_VS-313-add-an-instruction-event-and-view'),
]
r = requests.post('http://devsonar/api/projects/delete', headers=headers, data=files)

Related

errors from api when using python requests module for file upload

when i make the following request:
curl -X POST http://localhost:8080/endpoint \
-F "file=filepath/example.zip" \
-H "Content-Type: multipart/form-data"
everything works fine.
However, when i try to do the same in python:
import requests
url = "http://localhost:8080/endpoint"
files = {"file": open("example.zip", "rb")}
headers = {"Content-Type": "multipart/form-data"}
r = requests.post(url, files=files, headers=headers)
the api gives me an error. are these two code snippets not equivalent? thanks
the issue was this:
multipart data POST using python requests: no multipart boundary was found
it seems as though setting
headers = {"Content-Type": "multipart/form-data"}
caused the issue. i removed it and it works

Export POST Request from postman to Python

I am attempting to run a POST request in python. When I test in Postman it runs fine and imports the file. When I run it in Python I error out. This posts a spreadsheet stored on my computer into Quip. I am getting the error missing argument 'file'. I have tried moving several things around and am not sure what im missing. here is what im working with:
import requests
url = "https://platform.quip.com/1/threads/import-file"
payload = {'type': 'spreadsheet'}
files = [
('file', open('/Users/admin/Documents/excel/logs/Output/test.xlsx','rb'))
]
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer **************************='
}
response = requests.request("POST", url, headers=headers, data = payload, files = files)
print(response.text.encode('utf8'))
Try to change this line:
files = {'file': open('/Users/admin/Documents/excel/logs/Output/test.xlsx','rb')}
Also I noticed a bug in Postman (version 9.9.1): payload contains only the first field. Be aware of that.
So I would not rely on generated code.

file upload works with curl and postman but not python requests

I am attempting to upload a CSV file to an API (which gives me very little error info) using python requests library.
(I'm running Python 3.5 and using requests version 2.18.4 on OS X 10.11.6)
This curl command in terminal works fine: curl -F 'file=#/path/to/file.csv' myurl.com/upload -H "Authorization: TOKEN sometoken"
A multipart/form-data POST request from Postman also works, but I can't seem to make it work with the python requests library.
I've tried many variations of this request:
import requests
headers = {'Authorization': 'TOKEN sometoken', 'Content-Type': 'multipart/form-data'}
with open(file_path, 'rb') as f:
r = requests.post(myurl, headers=headers, data=f)
## I've also tried data={"file": f}
I get a status code of 200, but the response is {"success": "false"} (frustratingly unhelpful).
What am I missing in the python request compared to the curl request?
EDIT: it seems that the -F flag for the curl command emulates an HTML form that has been submitted...is there a way to do this with requests?
In your curl code you're using the -F parameter, which submits the data as a multipart message - in other words you're uploading a file.
With requests you can post files with the files parameter. An example:
import requests
headers = {'Authorization': 'TOKEN sometoken'}
data = {'file': open(file_path, 'rb')}
r = requests.post(myurl, headers=headers, files=data)
Note that requests creates the Content-Type header automatically, based on the submitted data.
The python open() function returns a file object. This is not what you want to send to the API.
Instead, you can use:
with open(file_path, 'rb') as f:
r = requests.post(myurl, headers=headers, data=f.read())
Syntax taken from here.

Converting CURL command to Python with requests

I want to use the requests library in Python to make a POST request.
But the API I'm trying to use uses curl to make the request and I don't know how to convert that.
This is the curl command:
curl -X POST "https://api/recognize?secret_key=abc" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "image=#/path/to/image.jpg;type=image/jpeg"
For the moment I'm just using a URL instead of the image itself as a workaround.
Code:
params = (
('image_url', '2015-BMW-320djpg'),
('secret_key', 'abc'),
)
response = requests.post('https://api/recognize_url', params=params)
As far as I'm aware there's no "cURL -> Requests" translator, but it should be fairly easy to translate that one request (and requests like it) to use the requests module.
files = {'image': open('/path/to/image.jpg', 'rb')}
params = {'secret_key': 'abc'}
headers = {'accept': 'application/json'}
response = requests.post(url, files=files, params=params, headers=headers)
First, paste your command into curlconverter.com/python/ and it will convert it to
import requests
headers = {
'accept': 'application/json',
}
params = {
'secret_key': 'abc',
}
files = {
'image': open('/path/to/image.jpg;type=image/jpeg', 'rb'),
}
response = requests.post('https://api/recognize', params=params, headers=headers, files=files)
Then, the 'image': open('/path/to/image.jpg;type=image/jpeg', 'rb'), line is wrong because the ;type=image/jpeg is not part of the file path to your image. To correct it, you need to read the curl documentation for the -F flag and the Requests documentation for the files= parameter (or just the Advanced Usage page) to know that you need to change it to
files = {
'image': ('image.jpg', open('/path/to/image.jpg', 'rb'), 'image/jpeg'),
}

How to use Python requests module and TeamCity API to trigger a build?

There is a cURL example in section Triggering a Build of TeamCity 9.x Documentation:
curl -v -u user:password http://teamcity.server.url:8111/app/rest/buildQueue --request POST --header "Content-Type:application/xml" --data-binary #build.xml
I'd like to know how to convert it into an equivalent Python script (using POST request from the requests module)?
BTW, I tried the following Python script but got such a response code 400 (Bad Request):
url = "http://myteamcity.com:8111/httpAuth/app/rest/buildQueue/"
headers = {'Content-Type': 'application/json'}
data = json.dumps({'buildTypeId': 'MyTestBuild'})
r = requests.post(url, headers=headers, data=data, auth=("username", "password"), timeout=10)
print "r = ", r
>> r = <Response [400]>
If change the Content-Type in headers into Accept, got another response code 415 (Unsupported Media Type):
headers = {'Accept': 'application/json'}
>> r = <Response [415]>
The documentation for triggering a build shows you need to send XML, not JSON:
<build>
    <buildType id="buildConfID"/>
</build>
The TeamCity REST API is a bit of a mixed bag; some methods accept both XML and JSON, some only accept XML. This is one of those latter methods. They'll respond with either XML or JSON, based on what you set the Accept header to.
Send the above with your required build ID; for an XML document that simply you could use templating:
from xml.sax.saxutils import quoteattr
template = '<build><buildType id={id}/></build>'
url = "http://myteamcity.com:8111/httpAuth/app/rest/buildQueue/"
headers = {'Content-Type': 'application/xml'}
build_id = 'MyTestBuild'
data = template.format(id=quoteattr(build_id))
r = requests.post(url, headers=headers, data=data, auth=("username", "password"), timeout=10)
Note that I used the xml.sax.saxutils.quotattr() function to make sure the value of build_id is properly quoted for inclusion as a XML attribute.
This'll produce XML; add 'Accept': 'application/json' to the headers dictionary if you want to process a JSON response.
FYI json request works in TeamCity 10.
Since this question was written & answered, a contemporary OSS alternative now exists: pyteamcity.
Pip command to install (or add pyteamcity to requirements.txt, etc.)
pip install pyteamcity
Code:
from pyteamcity import TeamCity
tc = TeamCity('username', 'password', 'server', 'port')
result = tc.trigger_build('build_id')
print(f'Build triggered. Web URL: {result['webUrl']}')

Categories