首页 > 解决方案 > Python 请求:多部分,其中一部分为 json

问题描述

Netflix 的 Genie API 采用JSONmultipart,其中一个部分request在.application/jsonattachmentapplication/octet-stream

Requests 让一个普通的 JSON POST 变得非常简单:

requests.post(
  url=self.host + self.endpoint,
  json={
    "version" : "1.0",
    "user" : "genie",
    "name" : "List * ... Directories bash job",
    "description" : "Genie 3 Test Job",
    "configs" : [ "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/config1" ],
    "dependencies" : [ "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/dep1" ],
    "setupFile" : "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/jobsetupfile",
    "commandArgs" : "-c 'echo hello world'",
    "clusterCriterias" : [ {
      "tags" : [ "localhost" ]
    } ],
    "commandCriteria" : [ "bash" ],
  },
)

命令是有限的,所以如果你有一个大的命令(查询)要发送,你最好使用附件。

对于请求,请求多部分也不难:

requests.post(
  url=self.host + self.endpoint,
  json={
    "version" : "1.0",
    "user" : "genie",
    "name" : "List * ... Directories bash job",
    "description" : "Genie 3 Test Job",
    "configs" : [ "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/config1" ],
    "dependencies" : [ "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/dep1" ],
    "setupFile" : "/home/travis/build/Netflix/genie/genie-web/build/resources/test/com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/job/jobsetupfile",
    "commandArgs" : "-c 'cat query.sql'",
    "clusterCriterias" : [ {
      "tags" : [ "localhost" ]
    } ],
    "commandCriteria" : [ "bash" ],
  },
  files={
    "attachment": (
      'query.sql',
      'select count(1) from small_table;',
      'application/octet-stream'
    ),
  },
)

除非,如果files存在,它将忽略json如果我更改jsondata它将是一种形式。我可以将 JSON dict 移动到filesdict 中,但它似乎没有作为 JSON 处理,现在我需要使用包对其进行编码吗?

我问是因为requests在参数和响应对象中处理 json 我怀疑它也会在多部分表单的某个地方处理它,否则我json只是为了json.dumps(...)

此外:

  1. attachment如果您需要多个附件,似乎没有一种方法可以发送多个名为 API 允许/期望的部分。[正如我的评论,这可以通过更改files为列表名称到文件对的列表来完成]。
  2. 示例请求显示部分标头具有未引用的名称,Content-Disposition: form-data; name=requestContent-Disposition: form-data; name=attachment请求包似乎是生成的Content-Disposition: form-data; name="attachment"

标签: pythonjsonpython-requestsmultipartform-data

解决方案


“我可以将 JSON 字典移动到文件字典中,但它似乎没有作为 JSON 处理”

  1. 您可以将 dict 转储到磁盘上的 JSON 文件。

tempfile.TemporaryFile可以使用。转储、请求、清理和重复

“否则我只为 json.dumps(...) 引入 json”

  1. 如果您需要保留 dict 并在运行期间构建请求(此用例忽略 1),则可以这样做。但是,请记住将转储转换为 io.BytesIO 对象,以便请求可以计算内容长度标头。

另外,请记住将文件的内容类型传递为“application/octet-stream”而不是“plain/text”

“示例请求显示部分标题的名称未加引号”

我不认为这应该重要。RFC 2183记录了长度 < 78 但包含 tspecials 的参数值表示为带引号的字符串。

虽然 name 参数的值不包括 tspecials,但对于短值 IMO,这是更可靠的处理。


推荐阅读