简体   繁体   English

如何将 JSON 作为多部分 POST 请求的一部分发送

[英]How to send JSON as part of multipart POST-request

I have following POST-request form (simplified):我有以下 POST 请求表格(简化版):

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 :我尝试发送带有requests POST 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:但它返回状态400并带有以下注释:

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. requests为您生成一个边界并将其设置在标头中,但如果您已经设置了标头,则生成的有效负载和标头将不匹配。 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`).请注意,我还为file部分指定了文件名( file路径的基本名称)。

For more information on multi-part POST requests, see the advanced section of the documentation .有关多部分 POST 请求的更多信息,请参阅文档高级部分

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:如果有人搜索准备使用的方法将 python dicts 转换为多部分形式的数据结构, 这里是一个简单的 gist 示例来进行这种转换:

{"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:要发送一些数据,您可能需要使用此 gist 中的multipartify方法,如下所示:

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:要与任何文件一起发送相同的数据(如 OP 所需),您可以简单地添加如下:

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.请注意, attachment是由服务器端点定义的名称,可能会有所不同。 Also attachment[0] indicates that it is first file in you request - this is also should be defined by API documentation.此外, attachment[0]表示它是您请求中的第一个文件 - 这也应该由 API 文档定义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM