在HTTP中,有两种方式可以发布数据:application/x-www-form-urlencoded和multipart/form-data。我知道大多数浏览器只能在使用multipart/form-data时上传文件。在API上下文中使用其中一种编码类型(不涉及浏览器)时,是否有任何额外的指导?例如,这可能基于:
application/x-www-form-urlencoded
multipart/form-data
到目前为止,我基本上没有在网络上找到关于使用不同内容类型的正式指导。
我不认为HTTP仅限于multipart或x-loo-form-urlencoded中的POST。内容类型页眉与HTTP POST方法正交(您可以填写适合您的MIME类型)。典型的基于超文本标记语言表示的Web应用程序也是如此(例如,json payload在传输ajax请求的payload时变得非常流行)。
关于基于HTTP的RESTful API,我接触到的最流行的内容类型是应用程序/xml和应用程序/json。
我会尝试将二进制数据表示为自己的资产/资源。它添加了另一个调用,但更好地解耦了东西。示例图像:
POST /imagesContent-type: multipart/mixed; boundary="xxxx"... multipart data 201 CreatedLocation: 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-loo-form-urlencoded"是发送大型二进制数据或文本的数量包含非ASCII字符。该内容类型“multipart/form-data”应用于提交表格包含文件、非ASCII数据、和二进制数据。
然而,对我来说,这将归结为工具/框架支持。
如果您清楚地了解您的用户以及他们将如何使用您的API,那么这将有助于您做出决定。如果您让API用户难以上传文件,那么他们就会离开,您将花费大量时间来支持他们。
其次是您编写API的工具支持,以及您适应一种上传机制而不是另一种上传机制的容易程度。
太长别读
摘要;如果您有二进制(非字母数字)数据(或显着大小的有效载荷)要传输,请使用multipart/form-data。否则,请使用application/x-www-form-urlencoded。
你提到的MIME类型是用户代理(浏览器)必须支持的HTTP POST请求的两个Content-Type标头。这两种类型请求的目的是向服务器发送名称/值对列表。根据传输的数据类型和数量,其中一种方法比另一种方法更有效。要理解原因,你必须看看每种方法背后在做什么。
Content-Type
对于application/x-www-form-urlencoded,发送到服务器的HTTP消息的主体本质上是一个巨大的查询字符串名称/值对由与号分隔(&),名称和值由等于符号分隔(=)。这方面的一个例子是:
&
=
MyVariableOne=ValueOne&MyVariableTwo=ValueTwo
根据产品规格:
[保留和]非字母数字字符替换为'%HH',百分号和两个十六进制数字表示字符的ASCII代码
这意味着对于存在于我们的一个值中的每个非字母数字字节,它将需要三个字节来表示它。对于大型二进制文件,将有效负载增加三倍将非常低效。
这就是multipart/form-data的用武之地。通过这种传输名称/值对的方法,每对都在MIME消息中表示为“部分”(如其他答案所述)。部分由特定的字符串边界分隔(特别选择,以便该边界字符串不会出现在任何“值”有效负载中)。每个部分都有自己的一组MIME标头,例如Content-Type,特别是Content-Disposition,它可以为每个部分提供其“名称”。每个名称/值对的值片是MIME消息每个部分的有效负载。MIME规范在表示值有效负载时为我们提供了更多选项——我们可以选择更有效的二进制数据编码来节省带宽(例如base 64甚至原始二进制)。
Content-Disposition
为什么不一直使用multipart/form-data?对于短字母数字值(像大多数Web表单一样),添加所有MIME标头的开销将大大超过更有效的二进制编码所节省的任何费用。
至少在这里阅读第一段!
我知道这晚了3年,但Matt(已接受)的答案是不完整的,最终会让你陷入困境。这里的关键是,如果你选择使用multipart/form-data,边界必须没有出现在服务器最终接收的文件数据中。
这对于application/x-www-form-urlencoded来说不是问题,因为没有边界。x-www-form-urlencoded也可以始终处理二进制数据,通过将一个任意字节转换为三个7BIT字节的简单权宜之计。效率低下,但它有效(请注意,关于无法发送文件名和二进制数据的注释是不正确的;您只需将其作为另一个键/值对发送即可)。
x-www-form-urlencoded
7BIT
multipart/form-data的问题是边界分隔符不能出现在文件数据中(参见rfc2388;第5.2节还包括一个相当蹩脚的借口,因为没有适当的聚合MIME类型来避免这个问题)。
因此,乍一看,multipart/form-data在任何文件上传中没有任何价值,二进制或其他方式。如果您没有正确选择边界,那么您的将最终会出现问题,无论您是发送纯文本还是原始二进制-服务器都会在错误的地方找到边界,您的文件将被截断,或者POST将失败。
关键是选择一种编码和边界,这样您选择的边界字符就不能出现在编码输出中。一个简单的解决方案是使用base64(没有使用原始二进制)。在bas64中,3个任意字节被编码成四个7位字符,其中输出字符集是[A-Za-z0-9+/=](即字母数字, '+', '/' 或 '='). =是特例,并且可能只出现在编码输出的末尾,作为单个=或双==。现在,选择您的边界作为不能出现在base64输出中的7位ASCII字符串。您在网上看到的许多选择都未通过此测试-例如,MDN表单文档在发送二进制数据时使用“blob”作为边界-不好。然而,类似“!blob!”的东西永远不会出现在base64输出中。
base64
[A-Za-z0-9+/=]
==
我正在为一家印刷厂做一个项目,由于将来自HTML5canvas元素的图像上传到服务器而遇到一些问题。我挣扎了至少一个小时,我没有把图像正确保存在我的服务器上。
canvas
一旦我设置了我的jQuery ajax调用application/x-www-form-urlencoded的contentType选项一切都以正确的方式进行,bas64编码的数据被正确解释并成功保存为图像。
contentType
也许这有助于别人!
如果您需要使用Content-Type=x-www. urlencoded-form,则不要使用FormDataCollection作为参数:在asp.netCore 2+FormDataCollection没有格式事项所需的默认构造函数。改用IFormCollection:
public IActionResult Search([FromForm]IFormCollection type){return Ok();}
在我的情况下,问题是响应contentType是application/x-www-form-urlencoded,但实际上它包含一个JSON作为请求的主体。当我们在Django中访问request.data时,它无法正确转换它,因此访问request.body。
JSON
request.data
request.body
请参考此答案以更好地理解:异常:从请求的数据流读取后无法访问主体