【问题标题】:Python Requests: Multipart with one part as jsonPython 请求:多部分,其中一部分为 json
【发布时间】:2019-05-22 02:31:25
【问题描述】:

Netflix 的 Genie API 采用 JSONmultipartapplication/json 中的一部分 requestapplication/octet-stream 中的任意数量的 attachment 部分。

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,如果我将json 更改为data,它将是一个表单。我可以将 JSON dict 移动到 files dict 中,但它似乎没有作为 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"

【问题讨论】:

  • 我的意思是我知道我可以将字典包装在 json.dumps(…) 中,但是 requests 在使用 json= 参数和 .json() 响应方法处理它方面做得非常好,我我错过了某种方式来支持多部分形式的 json 部分?对于#1,如果我使用文件列表而不是字典,我可以有多个[['attachment',('a','content','plain/text')],['attachment',('b','content','plain/text')]]

标签: python json python-requests multipartform-data


【解决方案1】:

“我可以将 JSON dict 移动到 files dict 中,但它似乎没有作为 JSON 处理”

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

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

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

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

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

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

我认为这无关紧要。 RFC 2183 记录长度 should 被表示为quoted-string。

虽然 name 参数的值不包括 tspecials,但这对于短值 IMO 的处理更为稳健。

【讨论】:

  • 感谢您的澄清;我确实更改了内容类型,但需要在某处将其包装为字节。所以我猜我并没有错过一种惯用的方式来包含 json 而无需专门将其转储到 json 中。
猜你喜欢
  • 2016-06-26
  • 2020-11-14
  • 1970-01-01
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多