How to send JSON as part of multipart POST-request
Question:
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?
Answers:
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.
In case if someone searches ready to use method to transform python dicts to multipart-form data structures here is a simple gist example to do such transformation:
{"some": ["balls", "toys"], "field": "value", "nested": {"objects": "here"}}
->
{"some[0]": "balls", "some[1]": "toys", "field": "value", "nested[objects]": "here"}
To send some data you may want to use the multipartify
method from this gist like this:
import requests # library for making requests
payload = {
"person": {"name": "John", "age": "31"},
"pets": ["Dog", "Parrot"],
"special_mark": 42,
} # Example payload
requests.post("https://example.com/", files=multipartify(payload))
To send same data along with any file (as OP wanted) you may simply add it like this:
converted_data = multipartify(payload)
converted_data["attachment[0]"] = ("file.png", b'binary-file', "image/png")
requests.post("https://example.com/", files=converted_data)
Note, that attachment
is a name defined by server endpoint and may vary. Also attachment[0]
indicates that it is first file in you request – this is also should be defined by API documentation.
I totally agree with @Martijn’s answer… but you can also do this way:
1. Sending request
import json
import requests
cover = 'superneat.jpg'
payload = {'title': 'The 100 (2014)', 'episodes': json.dumps(_episodes)}
files = [
('json', ('payload.json', json.dumps(payload), 'application/json')),
('cover', (cover, open(cover, 'rb')))
]
r = requests.post("https://superneatech.com/store/series", files=files)
print(r.text)
2. Receiving request
You will receive the JSON data as a file, get the content and continue…
Reference: View Here
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?
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.
In case if someone searches ready to use method to transform python dicts to multipart-form data structures here is a simple gist example to do such transformation:
{"some": ["balls", "toys"], "field": "value", "nested": {"objects": "here"}}
->
{"some[0]": "balls", "some[1]": "toys", "field": "value", "nested[objects]": "here"}
To send some data you may want to use the multipartify
method from this gist like this:
import requests # library for making requests
payload = {
"person": {"name": "John", "age": "31"},
"pets": ["Dog", "Parrot"],
"special_mark": 42,
} # Example payload
requests.post("https://example.com/", files=multipartify(payload))
To send same data along with any file (as OP wanted) you may simply add it like this:
converted_data = multipartify(payload)
converted_data["attachment[0]"] = ("file.png", b'binary-file', "image/png")
requests.post("https://example.com/", files=converted_data)
Note, that attachment
is a name defined by server endpoint and may vary. Also attachment[0]
indicates that it is first file in you request – this is also should be defined by API documentation.
I totally agree with @Martijn’s answer… but you can also do this way:
1. Sending request
import json
import requests
cover = 'superneat.jpg'
payload = {'title': 'The 100 (2014)', 'episodes': json.dumps(_episodes)}
files = [
('json', ('payload.json', json.dumps(payload), 'application/json')),
('cover', (cover, open(cover, 'rb')))
]
r = requests.post("https://superneatech.com/store/series", files=files)
print(r.text)
2. Receiving request
You will receive the JSON data as a file, get the content and continue…
Reference: View Here