How to send JSON as part of multipart POST-request

21,814

You are setting the header yourself, including a boundary. Don't do this; requests generates a boundary for you and sets it in the header, but if you already set the header then the resulting payload and the header will not match. Just drop you headers altogether:

def send_request():
    payload = {"param_1": "value_1", "param_2": "value_2"}
    files = {
         'json': (None, json.dumps(payload), 'application/json'),
         'file': (os.path.basename(file), open(file, 'rb'), 'application/octet-stream')
    }

    r = requests.post(url, files=files)
    print(r.content)

Note that I also gave the file part a filename (the base name of the file path`).

For more information on multi-part POST requests, see the advanced section of the documentation.

Share:
21,814
Andersson
Author by

Andersson

Got Sh1tOverflow exception

Updated on July 24, 2022

Comments

  • Andersson
    Andersson almost 2 years

    I have following POST-request form (simplified):

    POST /target_page HTTP/1.1  
    Host: server_IP:8080
    Content-Type: multipart/form-data; boundary=AaaBbbCcc
    
    --AaaBbbCcc
    Content-Disposition: form-data; name="json" 
    Content-Type: application/json
    
    { "param_1": "value_1", "param_2": "value_2"}
    
    --AaaBbbCcc
    Content-Disposition: form-data; name="file"; filename="..." 
    Content-Type: application/octet-stream
    
    <..file data..>
    --AaaBbbCcc--
    

    I try to send POST-request with requests:

    import requests
    import json
    
    file = "C:\\Path\\To\\File\\file.zip"
    url = 'http://server_IP:8080/target_page'
    
    
    def send_request():
        headers = {'Content-type': 'multipart/form-data; boundary=AaaBbbCcc'}
    
        payload = { "param_1": "value_1", "param_2": "value_2"}
    
        r = requests.post(url, files={'json': (None, json.dumps(payload), 'application/json'), 'file': (open(file, 'rb'), 'application/octet-stream')}, headers=headers)
    
        print(r.content)
    
    if __name__ == '__main__':
        send_request()
    

    but it returns status 400 with following comment:

    Required request part \'json\' is not present.
    The request sent by the client was syntactically incorrect.
    

    Please point on my mistake. What should I change to make it work?

  • jsbueno
    jsbueno about 8 years
    If anything the fact the O.P. is passing the boundary pattern inside an opaque string, and still expecting the library to reuse that pattern, should be a goog clue that something is wrong.
  • Khamidulla
    Khamidulla over 5 years
    Using this code snippet i was able to send request. However at django backend says AttributeError: 'WSGIRequest' object has no attribute 'data'. Do you have any idea why?
  • Martijn Pieters
    Martijn Pieters over 5 years
    @Khamidulla: that's not a problem with this code. Something on the Django server has an error, there is no data attribute on the Django Request object.
  • Khamidulla
    Khamidulla over 5 years
    @MartijnPieters I will publish my question and send you link later today. Thank you for you response.
  • Scaramouche
    Scaramouche about 4 years
    hello, is the data supposed to be received via body in the server? it's arriving empty, thank you
  • Martijn Pieters
    Martijn Pieters about 4 years
    @Scaramouche: yes, POST data (however it is encoded) is sent in the request body. Replace your URL with https://httpbin.org/post to receive a JSON response that echoes what you sent to verify that you are sending a valid request; this could be a server-side issue rather than a Requests issue.
  • JavaSa
    JavaSa almost 3 years
    @MartijnPieters; how this request would look if I have multiple files with one metadata json?
  • Martijn Pieters
    Martijn Pieters almost 3 years
    @JavaSa: Use a tuple with (partname, (filename, filedata, content_type)) elements, that way part names don't need to be unique . Do look at the documentation I linked to from my answer, which includes that format.