【问题标题】:application/x-www-form-urlencoded or multipart/form-data?应用程序/x-www-form-urlencoded 还是 multipart/form-data?
【发布时间】:2011-04-29 19:24:10
【问题描述】:

在 HTTP 中有两种 POST 数据的方式:application/x-www-form-urlencodedmultipart/form-data。我知道大多数浏览器只有在使用multipart/form-data 时才能上传文件。在 API 上下文(不涉及浏览器)中使用其中一种编码类型时是否有任何其他指导?这可能例如基于:

  • 数据大小
  • 存在非 ASCII 字符
  • 存在于(未编码的)二进制数据上
  • 需要传输其他数据(如文件名)

到目前为止,我基本上没有在网上找到关于使用不同内容类型的正式指南。

【问题讨论】:

  • 需要说明的是,这是 HTML 表单使用的两种 MIME 类型。 HTTP 本身没有这样的限制……人们可以通过 HTTP 使用任何他想要的 MIME 类型。

标签: http post http-headers


【解决方案1】:

我不认为 HTTP 仅限于 POST 多部分或 x-www-form-urlencoded。 Content-Type Header 与 HTTP POST 方法正交(您可以填写适合您的 MIME 类型)。这也是典型的基于 HTML 表示的 webapps 的情况(例如,json 有效负载在传输 ajax 请求的有效负载方面变得非常流行)。

关于基于 HTTP 的 Restful API,我接触到的最流行的内容类型是 application/xml 和 application/json。

应用程序/xml:

  • 数据大小:XML 非常冗长,但在使用压缩并认为写访问情况(例如通过 POST 或 PUT)比读访问更罕见(在许多情况下小于 3%所有流量)。很少有我必须优化写入性能的情况
  • 存在非 ascii 字符:您可以在 XML 中使用 utf-8 作为编码
  • 存在二进制数据:需要使用 base64 编码
  • 文件名数据:您可以将这个内部字段封装在 XML 中

应用程序/json

  • 数据大小:比 XML 更紧凑,仍然是文本,但您可以压缩
  • 非 ascii 字符:json 是 utf-8
  • 二进制数据:base64(另见json-binary-question
  • 文件名数据:在json中封装为自己的字段部分

二进制数据作为自己的资源

我会尝试将二进制数据表示为自己的资产/资源。它增加了另一个调用,但更好地解耦了东西。示例图片:

POST /images Content-type: multipart/mixed; boundary="xxxx" ... multipart data 201 Created Location: http://imageserver.org/../foo.jpg

在以后的资源中,您可以简单地将二进制资源作为链接内联:

<main-resource&gt ... <link href="http://imageserver.org/../foo.jpg"/> </main-resource>

【讨论】:

  • 有趣。但是何时使用 application/x-www-form-urlencoded 以及何时使用 multipart/form-data?
  • application/x-www-form-urlencoded 是您请求的默认 mime 类型(另请参阅 w3.org/TR/html401/interact/forms.html#h-17.13.4)。我将它用于“普通”网络表单。对于 API,我使用 application/xml|json。 multipart/form-data 是对附件的思考(在响应正文中,几个数据部分与定义的边界字符串连接)。
  • 我认为 OP 可能只是询问 HTML 表单使用的两种类型,但我很高兴有人指出了这一点。
  • 您是否尝试过浏览器是否可以提交表单字段,例如使用 Json-Mime 类型?
【解决方案2】:

我同意曼努埃尔所说的很多。其实他的cmets就是指这个url...

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

...其中指出:

内容类型 “应用程序/x-www-form-urlencoded”是 发送大数据效率低下 大量的二进制数据或文本 包含非 ASCII 字符。这 内容类型“多部分/表单数据” 应该用于提交表格 包含文件、非 ASCII 数据、 和二进制数据。

但是,对我来说,这将归结为工具/框架支持。

  • 你有什么工具和框架 期望您的 API 用户正在构建 他们的应用程序与?
  • 他们有 他们可以使用的框架或组件 有利于一种方法而不是 其他?

如果您清楚地了解您的用户,以及他们将如何使用您的 API,那么这将有助于您做出决定。如果您让 API 用户难以上传文件,那么他们就会离开,您将花费大量时间来支持他们。

其次是您在编写 API 时所拥有的工具支持,以及适应一种上传机制而不是另一种上传机制的难易程度。

【讨论】:

  • 嗨,这是否意味着每次我们向网络服务器发布内容时,我们都必须提及 Content-type 是什么,以便让网络服务器知道它是否应该解码数据?即使我们自己制作 http 请求,我们也必须提到 Content-type 对吗?
  • @GMsoF,这是可选的。见stackoverflow.com/a/16693884/632951。在为特定服务器制作特定请求时,您可能希望避免使用内容类型,以避免一般开销。
【解决方案3】:

TL;DR

总结;如果您有二进制(非字母数字)数据(或相当大的有效载荷)要传输,请使用multipart/form-data。否则,请使用application/x-www-form-urlencoded


您提到的 MIME 类型是用户代理(浏览器)必须支持的 HTTP POST 请求的两个 Content-Type 标头。这两种类型的请求的目的都是向服务器发送名称/值对列表。根据正在传输的数据的类型和数量,其中一种方法将比另一种更有效。要了解原因,您必须了解每个人在幕后所做的事情。

对于application/x-www-form-urlencoded,发送到服务器的 HTTP 消息的主体本质上是一个巨大的查询字符串——名称/值对由 & 符号 (&amp;) 分隔,名称与值由等号分隔符号 (=)。例如:

MyVariableOne=ValueOne&amp;MyVariableTwo=ValueTwo

根据specification

[保留和]非字母数字字符被替换为 `%HH'、一个百分号和两个表示字符 ASCII 码的十六进制数字

这意味着对于我们的值中存在的每个非字母数字字节,将需要三个字节来表示它。对于大型二进制文件,将有效负载增加三倍将非常低效。

这就是multipart/form-data 的用武之地。使用这种传输名称/值对的方法,每对都表示为 MIME 消息中的一个“部分”(如其他答案所述)。部分由特定的字符串边界分隔(特别选择以便此边界字符串不会出现在任何“值”有效负载中)。每个部分都有自己的一组 MIME 标头,例如 Content-Type,尤其是 Content-Disposition,它可以为每个部分指定其“名称”。每个名称/值对的值片段是 MIME 消息每个部分的有效负载。 MIME 规范在表示值负载时为我们提供了更多选择——我们可以选择更有效的二进制数据编码以节省带宽(例如 base 64 甚至原始二进制)。

为什么不一直使用multipart/form-data?对于较短的字母数字值(如大多数 Web 表单),添加所有 MIME 标头的开销将大大超过更有效的二进制编码所节省的成本。

【讨论】:

  • x-www-form-urlencoded 有长度限制,还是没有限制?
  • @Pacerier 该限制由接收 POST 请求的服务器强制执行。有关更多讨论,请参阅此线程:stackoverflow.com/questions/2364840/…
  • @ZiggyTheHamster JSON 和 BSON 对于不同类型的数据都更有效。对于这两种序列化方法,Base64 不如 gzip。 Base64 根本没有带来任何优势,HTTP 支持二进制 pyload。
  • 另请注意,如果表单包含命名文件上传,您唯一的选择是 form-data,因为 urlencoded 无法放置文件名(在 form-data 中,它是 name 参数内容处置)。
  • @EML 看到我的括号“(专门选择以便此边界字符串不会出现在任何“值”有效负载中)”
【解决方案4】:

在这里至少阅读第一段!

我知道这已经晚了 3 年,但马特(已接受)的答案不完整,最终会给您带来麻烦。这里的关键是,如果你选择使用multipart/form-data,边界一定不能出现在服务器最终接收到的文件数据中。

这对application/x-www-form-urlencoded 来说不是问题,因为没有边界。 x-www-form-urlencoded 也可以始终处理二进制数据,方法是将一个任意字节转换为三个7BIT 字节。效率低下,但它有效(请注意,关于无法发送文件名和二进制数据的评论是不正确的;您只需将其作为另一个键/值对发送)。

multipart/form-data 的问题是边界分隔符不能出现在文件数据中(请参阅RFC 2388;第 5.2 节还包括一个相当蹩脚的借口,即没有适当的聚合 MIME 类型来避免这个问题)。

因此,乍一看,multipart/form-data任何文件上传中没有任何价值,无论是二进制文件还是其他文件。如果您没有正确选择边界,那么无论您发送纯文本还是原始二进制文件, 最终都会遇到问题 - 服务器会在错误的位置找到边界,而您的文件将被截断,否则 POST 将失败。

关键是选择编码和边界,使您选择的边界字符不能出现在编码输出中。一种简单的解决方案是使用base64 使用原始二进制文件)。在base64 中,3 个任意字节被编码为四个 7 位字符,其中输出字符集为[A-Za-z0-9+/=](即字母数字、'+'、'/' 或 '=')。 = 是一种特殊情况,可能只出现在编码输出的末尾,作为单个= 或双==。现在,选择您的边界作为 7 位 ASCII 字符串,该字符串不能出现在 base64 输出中。您在网上看到的许多选择都未能通过此测试 - 例如,MDN 表单 docs 在发送二进制数据时使用“blob”作为边界 - 不好。但是,像“!blob!”之类的东西永远不会出现在base64 输出中。

【讨论】:

  • 虽然考虑 multipart/form-data 是为了确保边界不会出现在数据中,但通过选择足够长的边界来实现这一点相当简单。请不要使用 base64 编码来完成此操作。随机生成且与 UUID 长度相同的边界就足够了:stackoverflow.com/questions/1705008/…
  • @EML,这根本没有意义。显然,边界是由 http 客户端(浏览器)自动选择的,客户端将足够聪明,不会使用与上传文件的内容冲突的边界。就像一个子字符串匹配 index === -1 一样简单。
  • @Pacerier:(A)阅读问题:“不涉及浏览器,API 上下文”。 (B) 浏览器无论如何都不会为您构建请求。你自己做,手动。浏览器没有魔法。
  • @BeniBela,那时他可能会建议使用'()+-./:=。然而,带有子字符串检查的随机生成仍然是要走的路,它可以用一行来完成:while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}。 EML 的建议(转换为 base64 只是为了避免匹配子字符串)很奇怪,更不用说它带来了不必要的性能下降。由于单线算法同样简单明了,因此所有的麻烦都是徒劳的。 Base64 并不意味着(ab)以这种方式使用,因为 HTTP 正文 accept all 8-bit 八位字节。
  • 这个答案不仅没有增加讨论,而且给出了错误的建议。首先,无论何时在分离的部分中传输随机数据,所选择的边界总是有可能出现在有效载荷中。确保不会发生这种情况的唯一方法是检查我们提出的每个边界的整个有效负载。完全不切实际。我们只接受 infinitesimal 的碰撞概率并得出一个合理的边界,例如“---boundary--boundary---”。其次,总是使用 Base64 会浪费带宽并无缘无故地填满缓冲区。
【解决方案5】:

关于上传 HTML5 画布图像数据的一点提示:

我正在为一家印刷店开发一个项目,但由于将来自 HTML5 canvas 元素的图像上传到服务器而遇到了一些问题。我挣扎了至少一个小时,但无法将图像正确保存在我的服务器上。

一旦我设置 contentType 我的 jQuery ajax 调用选项application/x-www-form-urlencoded 一切正常,base64 编码的数据被正确解释并成功保存为图像。


也许这对某人有帮助!

【讨论】:

  • 在您更改之前发送的内容类型是什么?此问题可能是由于服务器不支持您发送的内容类型。
【解决方案6】:

如果您需要使用 Content-Type=x-www-urlencoded-form 则不要使用 FormDataCollection 作为参数:在 asp.net Core 2+ 中,FormDataCollection 没有格式化程序所需的默认构造函数。改用 IFormCollection:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-07
    • 1970-01-01
    • 2018-10-19
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    相关资源
    最近更新 更多