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

Posted

技术标签:

【中文标题】如何发送 JSON 作为多部分 POST 请求的一部分【英文标题】:How to send JSON as part of multipart POST-request 【发布时间】:2016-06-26 16:06:29 【问题描述】:

我有以下 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--

我尝试使用 requests 发送 POST 请求:

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()

但它返回状态 400 并带有以下注释:

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

请指出我的错误。我应该改变什么才能让它工作?

【问题讨论】:

您需要注明Content-Type: application/json @noctilux:不适合你不知道的多部分帖子。 不要自己设置Content-type 标头,留给requests 生成。 在***.com/questions/19439961/…中据说不将json部分编码为json作为一种解决方法 如果你没有固定在 pyrequests 上,你可以使用 libcurlPycURL (pycurl.io/docs/latest)。在他的线程中是一个在 cURL 中使用 json 的多部分 POST 的工作示例:***.com/questions/29231926/… 【参考方案1】:

您自己设置标题,包括边界。不要这样做; requests 为您生成边界并将其设置在标头中,但如果您已经设置标头,则生成的有效负载和标头将不匹配。完全删除标题:

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)

请注意,我还为 file 部分指定了文件名(file 路径的基本名称)。

有关多部分 POST 请求的详细信息,请参阅advanced section of the documentation。

【讨论】:

如果 O.P. 在不透明字符串中传递边界模式,并且仍然希望库重用该模式,那么这应该是一个错误的线索。 使用此代码 sn-p 我能够发送请求。然而在 django 后端说AttributeError: 'WSGIRequest' object has no attribute 'data'。你知道为什么吗? @Khamidulla:这段代码没有问题。 Django服务器上的东西有错误,Django Request object上没有data属性。 @Scaramouche:是的,POST 数据(不管它是如何编码的)是在请求正文中发送的。将您的 URL 替换为 https://httpbin.org/post 以接收与您发送的内容相呼应的 JSON 响应,以验证您发送的请求是否有效;这可能是服务器端问题,而不是请求问题。 @JavaSa:使用带有 (partname, (filename, filedata, content_type)) 元素的元组,这样部件名称就不需要是唯一的。请查看我从答案中链接到的文档,其中包括该格式。【参考方案2】:

如果有人搜索准备使用的方法来将 python dicts 转换为多部分形式的数据结构here 是一个简单的 gist 示例来进行这种转换:

"some": ["balls", "toys"], "field": "value", "nested": "objects": "here"
    ->
"some[0]": "balls", "some[1]": "toys", "field": "value", "nested[objects]": "here"

要发送一些数据,您可能需要使用此要点中的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))

要与任何文件一起发送相同的数据(根据 OP 的需要),您只需像这样添加它:

converted_data = multipartify(payload)
converted_data["attachment[0]"] = ("file.png", b'binary-file', "image/png")

requests.post("https://example.com/", files=converted_data)

请注意,attachment 是由服务器端点定义的名称,可能会有所不同。此外attachment[0] 表示它是您请求中的第一个文件 - 这也应该由 API 文档定义。

【讨论】:

以上是关于如何发送 JSON 作为多部分 POST 请求的一部分的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 3 中使用 Alamofire 在 POST 请求中将给定的 JSON 作为参数发送?

带有多部分请求的 Spring Boot 不受支持的媒体类型

如何使用 C# 在 POST 请求中发送 json 数据 [重复]

在 IIS 上发布包含 jpg 和 json 的多部分请求会导致内部服务器错误和 win32 状态 64

如何在 Spring App 中接收多部分请求

ReactJS:无法在一次 POST 调用中发送 JSON 数据和 PDF 文件