application / x-www-form-urlencoded或multipart / form-data?

时间:2010-10-24 11:12:02

标签: http post http-headers

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

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

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

6 个答案:

答案 0 :(得分:1840)

<强> TL; DR

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


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

对于application/x-www-form-urlencoded,发送到服务器的HTTP消息的主体实际上是一个巨大的查询字符串 - 名称/值对由&符号(&)分隔,名称与值乘以等号(=)。这方面的一个例子是:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

根据specification

  

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

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

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

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

答案 1 :(得分:128)

请至少阅读第一个PARA!

我知道这已经太晚了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个任意字节被编码为4个7位字符,其中输出字符集为[A-Za-z0-9+/=](即字母数字,&#39; +&#39;,&#39; /&# 39;或&#39; =&#39;)。 =是一种特殊情况,可能只会出现在编码输出的末尾,如单=或双==。现在,选择您的边界作为7位ASCII字符串,该字符串不能出现在base64输出中。您在网上看到的许多选择都无法通过此测试 - 例如,MDN表单docs,使用&#34; blob&#34;作为发送二进制数据的边界 - 不好。但是,像&#34;!blob!&#34;将永远不会出现在base64输出中。

答案 2 :(得分:87)

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

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

应用/ XML:

  • data-size:XML非常详细,但在使用压缩时通常不会出现问题,并认为写访问案例(例如通过POST或PUT)作为读访问更为罕见(在很多情况下它是&lt; 3所有流量的百分比)。很少有我必须优化写入性能的情况
  • 非ascii字符的存在:你可以使用utf-8作为XML中的编码
  • 存在二进制数据:需要使用base64编码
  • 文件名数据:您可以将此内部字段封装在XML

应用/ JSON

  • 数据大小:比XML更紧凑,仍然是文本,但你可以压缩
  • non-ascii chars:json是utf-8
  • 二进制数据:base64(另见json-binary-question
  • filename data:在json
  • 中封装为自己的field-section

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

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

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg  

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

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

答案 3 :(得分:29)

我同意曼努埃尔的说法。事实上,他的评论指的是这个网址......

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

......其中说明:

  

内容类型   “application / x-www-form-urlencoded”是   发送大的效率低下   二进制数据或文本的数量   包含非ASCII字符。该   内容类型“multipart / form-data”   应该用于提交表格   包含文件,非ASCII数据,   和二进制数据。

然而,对我而言,它将归结为工具/框架支持。

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

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

这将是您编写API的工具支持,以及您可以轻松地将一个上传机制容纳在另一个上面。

答案 4 :(得分:2)

从我这边稍微提示上传HTML5画布图像数据:

我正在为一家打印店工作,由于将图像上传到来自HTML5 canvas元素的服务器,因此出现了一些问题。我挣扎了至少一个小时,我没有得到它在我的服务器上正确保存图像。

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

也许这有助于某人!

答案 5 :(得分:1)

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

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