在HTTP中,有两种发布数据的方式:application/x-www-form-urlencoded
和multipart/form-data
。我了解大多数浏览器仅multipart/form-data
在使用后才能上传文件。在API上下文中使用一种编码类型(不涉及浏览器)时,是否还有其他指导?例如,这可能基于:
- 资料大小
- 存在非ASCII字符
- 存在于(未编码的)二进制数据上
- 需要传输其他数据(如文件名)
到目前为止,我基本上没有找到关于使用不同内容类型的正式指南。
在HTTP中,有两种发布数据的方式:application/x-www-form-urlencoded
和multipart/form-data
。我了解大多数浏览器仅multipart/form-data
在使用后才能上传文件。在API上下文中使用一种编码类型(不涉及浏览器)时,是否还有其他指导?例如,这可能基于:
到目前为止,我基本上没有找到关于使用不同内容类型的正式指南。
Answers:
TL; DR
摘要; 如果您要传输二进制(非字母数字)数据(或有效载荷大小很大),请使用multipart/form-data
。否则,请使用application/x-www-form-urlencoded
。
您提到的MIME类型Content-Type
是用户代理(浏览器)必须支持的HTTP POST请求的两个标头。这两种类型的请求的目的都是将名称/值对的列表发送到服务器。根据要传输的数据的类型和数量,其中一种方法将比另一种更为有效。要了解原因,您必须查看每个任务的幕后工作。
对于application/x-www-form-urlencoded
,发送到服务器的HTTP消息的主体实质上是一个巨大的查询字符串-名称/值对用&符(&
)分隔,名称与值用等号(=
)分隔。例如:
MyVariableOne=ValueOne&MyVariableTwo=ValueTwo
根据规格:
[保留和]非字母数字字符替换为'%HH',百分号和两个十六进制数字,代表该字符的ASCII码
这意味着对于我们的值之一中存在的每个非字母数字字节,将需要三个字节来表示它。对于大的二进制文件,将有效载荷增加三倍将是非常低效的。
就是这样multipart/form-data
。通过这种传输名称/值对的方法,每个对在MIME消息中都表示为“部分”(如其他答案所述)。部件由特定的字符串边界分隔(经过专门选择,以使该边界字符串不会出现在任何“值”有效负载中)。每个部分都有其自己的MIME标头集,例如Content-Type
和,特别是Content-Disposition
,可以为每个部分赋予其“名称”。每个名称/值对的值片是MIME消息的每个部分的有效负载。MIME规范在表示值有效载荷时为我们提供了更多选项-我们可以选择更有效的二进制数据编码来节省带宽(例如,基本64位甚至原始二进制)。
为什么不一直使用multipart/form-data
?对于短的字母数字值(像大多数Web表单一样),添加所有MIME标头的开销将大大超过更有效的二进制编码所节省的费用。
在这里至少阅读第一段!
我知道这已经晚了3年,但是Matt(接受)的答案不完整,最终会给您带来麻烦。这里的关键是,如果选择使用multipart/form-data
,则边界不得出现在服务器最终接收到的文件数据中。
对于而言application/x-www-form-urlencoded
,这不是问题,因为没有边界。x-www-form-urlencoded
通过将一个任意字节变成三个7BIT
字节的简单权宜之计,也可以始终处理二进制数据。效率低下,但有效(并且请注意,关于无法发送文件名和二进制数据的评论是不正确的;您只是将其作为另一个键/值对发送)。
问题multipart/form-data
在于边界分隔符一定不能出现在文件数据中(请参阅RFC 2388;第5.2节还包括一个相当la脚的借口,因为它没有避免此问题的适当的汇总MIME类型)。
因此,乍看multipart/form-data
之下,在任何文件上传(二进制或其他方式)中都没有任何价值。如果不正确地选择你的边界,那么你会最终有一个问题,不管你是发送纯文本或原始二进制-服务器会发现放错了地方的边界,你的文件将被截断,或POST将失败。
关键是选择一种编码和边界,以使您选择的边界字符不会出现在编码输出中。一种简单的解决方案是使用base64
(不使用原始二进制文件)。在base64中,将 3个任意字节编码为四个7位字符,其中输出字符集为[A-Za-z0-9+/=]
(即字母数字,“ +”,“ /”或“ =”)。=
是一种特殊情况,可能只在编码输出的末尾显示为single =
或double ==
。现在,选择您的边界作为7位ASCII字符串,该字符串不能出现在base64
输出中。您在网上看到的许多选择都无法通过此测试-MDN表单文档,例如,在发送二进制数据时使用“ blob”作为边界-不好。但是,类似“!blob!”之类的东西。永远不会出现在base64
输出中。
index === -1
。
'()+-./:=
然后使用。然而,通过子字符串检查随机生成仍然是可行的方法,并且只需一行即可完成:while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}
。EML的建议(为了避免匹配子字符串而转换为base64)只是很奇怪,更不用说它会带来不必要的性能下降。一站式算法同样简单明了,所有麻烦全都解决了。因为HTTP主体接受所有8位八位字节,所以不打算以这种方式使用Base64 。
我不认为HTTP限于多部分或x-www-form-urlencoded的POST。的内容类型标头是垂直于HTTP POST方法(可以补MIME类型适合你)。典型的基于HTML表示的Web应用程序也是如此(例如json有效负载非常流行,用于为ajax请求传输有效负载)。
关于基于HTTP的Restful API,我接触的最流行的内容类型是application / xml和application / json。
我会尝试将二进制数据表示为自己的资产/资源。它增加了另一个调用,但可以更好地解耦。图片示例:
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>
我同意曼努埃尔所说的话。实际上,他的评论指的是这个网址...
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的工具支持,以及与另一种上传机制相比,容纳一种上载机制有多么容易。