Python 请求:多部分,其中一个部分为 json

Python Requests: Multipart with one part as json

Netflix 的精灵 API 将 JSON or multipartapplication/json 中的一个部分 requestapplication/octet-stream 中任意数量的 attachment 部分相结合。

请求使一个简单的 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,如果我将 json 更改为 data,它将是一个表单。我可以将 JSON 字典移动到 files 字典中,但它似乎没有像 JSON 那样处理,现在我需要使用包对其进行编码?

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

此外:

  1. 似乎没有办法发送多个名为 attachment 的部分,如果您需要多个附加文件,api 会 allow/expect。 [在我的评论中,这可以通过将 files 更改为列表 name-to-file 对的列表来完成。
  2. 示例请求显示部分 headers 的名称未加引号,例如 Content-Disposition: form-data; name=requestContent-Disposition: form-data; name=attachment,而请求包似乎生成 Content-Disposition: form-data; name="attachment"

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

  1. You can dump the dict to JSON files on disk.

tempfile.TemporaryFile can of use. Dump, request, clean and repeat

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

  1. It is okay to do this if you need to keep the dict around and the request built during run (ignore 1 for this use-case). However, remember to convert the dumps to io.BytesIO objects so that requests can compute the content-length header.

Also, remember to pass the content type for the files as "application/octet-stream" and not "plain/text"

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

I don't think that it should matter. RFC 2183 documents that parameter values of length < 78 but containing tspecials should be represented as quoted-string.

While the value for the name parameter doesn't include tspecials, this is more robust handling for short values IMO.