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

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

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


当前回答

TL;博士

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


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

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

MyVariableOne=值一和MyVariableTwo=值二

根据规范:

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

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

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

为什么不一直使用多部分/表单数据?对于短字母数字值(像大多数web表单一样),添加所有MIME标头的开销将大大超过更高效的二进制编码所节省的成本。

其他回答

至少读第一段!

我知道这已经晚了3年,但马特(被接受)的答案是不完整的,最终会让你陷入麻烦。这里的关键是,如果选择使用多部分/表单数据,则服务器最终接收的文件数据中不能出现边界。

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

多部分/表单数据的问题是,文件数据中不能存在边界分隔符(请参阅RFC 2388;第5.2节还包含了一个相当蹩脚的借口,即没有适当的聚合MIME类型来避免此问题)。

因此,乍一看,多部分/表单数据在任何文件上载(二进制或其他)中都没有任何价值。如果您没有正确选择边界,那么无论您发送的是纯文本还是原始二进制文件,最终都会遇到问题-服务器将在错误的位置找到边界,文件将被截断,或者POST将失败。

关键是选择编码和边界,以使选定的边界字符不会出现在编码输出中。一个简单的解决方案是使用base64(不要使用原始二进制)。在base64中,3个任意字节被编码为4个7位字符,其中输出字符集为[A-Za-z0-9+/-=](即字母数字、“+”、“/”或“=”)。=是一种特殊情况,并且只能出现在编码输出的末尾,作为单=或双=。现在,选择边界作为7位ASCII字符串,它不能出现在base64输出中。你在网上看到的许多选择都没有通过这个测试——例如,MDN表单文档在发送二进制数据时使用“blob”作为边界——这是不好的。然而,类似“!blob!”的内容永远不会出现在base64输出中。

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

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

TL;博士

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


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

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

MyVariableOne=值一和MyVariableTwo=值二

根据规范:

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

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

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

为什么不一直使用多部分/表单数据?对于短字母数字值(像大多数web表单一样),添加所有MIME标头的开销将大大超过更高效的二进制编码所节省的成本。

我同意曼努埃尔所说的很多话。事实上,他的评论是指这个url。。。

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

…其中指出:

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

然而,对我来说,这取决于工具/框架支持。

您使用哪些工具和框架期望您的API用户正在构建他们的应用程序?他们有吗他们可以使用的框架或组件这种方法比另外

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

其次是您编写API的工具支持,以及如何轻松地适应一种上传机制。

只是我这边上传HTML5画布图像数据的一点提示:

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

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


也许这对某人有帮助!