关于http:application / x-www-form-urlencoded或multipart / form-data?

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

在HTTP中,发布数据有两种方法:application/x-www-form-urlencodedmultipart/form-data。我知道大多数浏览器只能在使用multipart/form-data的情况下上载文件。当在API上下文中使用其中一种编码类型(不涉及浏览器)时,是否有其他指导?这可能是基于:

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

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


DR

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

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

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

埃多克斯1〔6〕

根据规范:

[Reserved and] non-alphanumeric characters are replaced by `%HH', a percent sign and two hexadecimal digits representing the ASCII code of the character

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

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

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


至少在这里读第一段!

我知道这已经晚了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在任何文件上传(二进制或其他)中都没有任何价值。如果您没有正确选择边界,那么无论您发送的是纯文本还是原始二进制文件,最终都会出现问题-服务器将在错误的位置找到边界,并且您的文件将被截断,否则发布将失败。

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


我认为HTTP不局限于以multipart或x-www-form-urlencoded方式发布。Content-Type头与HTTP Post方法是正交的(您可以填充适合您的mime类型)。这也是典型的基于HTML表示的webapps的情况(例如,json负载对于为Ajax请求传输负载变得非常流行)。

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

应用程序/XML:

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

应用程序/JSON

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

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

我会尝试将二进制数据表示为自己的资产/资源。它添加了另一个调用,但更好地分离了内容。示例图像:

1
2
3
4
5
6
<wyn>POST /images
Content-type: multipart/mixed; boundary="xxxx"
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg</wyn>

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

1
2
3
4
<wyn><main-resource&gt
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource></wyn>


我同意曼纽尔所说的许多话。事实上,他的评论提到了这个网址…

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

…其中规定:

The content type
"application/x-www-form-urlencoded" is
inefficient for sending large
quantities of binary data or text
containing non-ASCII characters. The
content type"multipart/form-data"
should be used for submitting forms
that contain files, non-ASCII data,
and binary data.

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

  • 你用什么工具和框架希望您的API用户正在构建他们的应用程序?
  • 有吗他们可以使用的框架或组件有一种方法比其他?

如果你对你的用户有一个清晰的概念,以及他们将如何使用你的API,那么这将帮助你做出决定。如果你让你的API用户很难上传文件,那么他们就会离开,而你会花很多时间来支持他们。

其次是对编写API的工具支持,以及如何轻松地将一个上传机制容纳到另一个上传机制中。


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

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

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

也许这对某人有帮助!


如果需要使用content type=x-www-urlencoded-form,则不要将formdatacollection用作参数:在ASP.NET Core 2+formdatacollection中,没有格式化程序所需的默认构造函数。改用iformCollection:

1
2
3
4
 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }