3

I am getting http code 400 (bad request) when I am trying to post a file to server using python requests library.

Corresponding curl request which is successful:

curl -X POST -i https://de.api.labs.sophos.com/analysis/file/static/v1 \
-H 'Authorization: auth_string' \
-H 'Content-Type: multipart/form-data' \
-F "file=@filename"

API documentation: https://api.labs.sophos.com/doc/analysis/file/static.html

Can someone help me out what I might be doing wrong?
My code so far:

import requests

url = "https://de.api.labs.sophos.com/analysis/file/static/v1"
headers = {'content-type': 'multipart/form-data', 'Authorization': authorization}

with open(filepath, 'rb') as f:      
    files = {'file': f}  # Even tried {'file': f.read()}
    r = requests.post(url, files=files, headers=headers)
    if r.status_code in [200, 202]:
        return r.json()
    else:
        return r
0

1 Answer 1

6

TL;DR

Try to do it this way:

import requests

url = "https://de.api.labs.sophos.com/analysis/file/static/v1"
headers = {'Authorization': authorization}  # no Content-Type here

r = requests.post(url, headers=headers, files={"file": open(filepath, "rb")})
print(r.status_code, r.text)

Why

You shouldn't set Content-Type header manually when posting files with requests.

There are 2 reasons why:

  • requests will set Content-Type to multipart/form-data implicitly before making an actual HTTP request (as it does for Content-Length for example)
  • When using Content-Type: multipart/form-data you should specify a boundary as well. If no boundary is set, server will not be able to read data from request body correctly. So boundary is a required part of Content-Type header, if you use multipart/form-data.

In your example you haven't set boundary for the request. The fact is that requests does not set it for you if you override Content-Type header (which you do). And then server is not able to read your file in request body. Therefore, it returns you 400 Bad Request.

You can check it by typing print(r.request.headers["Content-Type"]) after you've made your request. It will output this:

multipart/form-data

, but it must look like this instead:

multipart/form-data; boundary=6387a52fb4d1465310a2b63b2d1c6e70

On the other hand, curl adds boundary implicitly, so you everything is fine and you receive 200 OK.

You can check it as well:

curl -H 'Content-Type: multipart/form-data' -F "[email protected]" -v http://httpbin.org/post

Which outputs:

* Connected to httpbin.org (34.230.136.58) port 80 (#0)
> POST /post HTTP/1.1
> Host: httpbin.org
> User-Agent: curl/7.47.0
> Content-Type: multipart/form-data; boundary=------------------------d257f5f4377a3997
...
Sign up to request clarification or add additional context in comments.

1 Comment

Wow !!! It worked without content-type. Thank You Ivan, for such a detailed answer :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.