在HTTP中,POST数据有两种方式:application/x-wwww-form-urlencoded和multipart/form-data。我知道大多数浏览器只能在使用多部分/表单数据的情况下上传文件。在API上下文中何时使用其中一种编码类型(不涉及浏览器),是否有其他指导?这可能例如基于:
数据大小存在非ASCII字符存在于(未编码的)二进制数据上需要传输其他数据(如文件名)
到目前为止,我基本上没有在网络上找到关于不同内容类型使用的正式指南。
在HTTP中,POST数据有两种方式:application/x-wwww-form-urlencoded和multipart/form-data。我知道大多数浏览器只能在使用多部分/表单数据的情况下上传文件。在API上下文中何时使用其中一种编码类型(不涉及浏览器),是否有其他指导?这可能例如基于:
数据大小存在非ASCII字符存在于(未编码的)二进制数据上需要传输其他数据(如文件名)
到目前为止,我基本上没有在网络上找到关于不同内容类型使用的正式指南。
当前回答
我认为HTTP不限于多部分或x-www-form-urlencoded的POST。Content-TypeHeader与HTTPPOST方法正交(您可以填写适合您的MIME类型)。这也是典型的基于HTML表示的Web应用程序的情况(例如,json有效负载对于传输ajax请求的有效负载变得非常流行)。
关于HTTP上的Restful API,我接触到的最流行的内容类型是application/xml和application/json。
应用程序/xml:
数据大小:XML非常冗长,但在使用压缩时通常不会出现问题,并且认为写访问情况(例如通过POST或PUT)比读访问更罕见(在许多情况下,它小于所有流量的3%)。很少有我必须优化写性能的情况存在非ascii字符:可以使用utf-8作为XML编码二进制数据的存在:需要使用base64编码文件名数据:可以在XML中封装此内部字段
应用程序/json
数据大小:比XML更紧凑,仍然是文本,但可以压缩非ascii字符:json是utf-8二进制数据:base64(另见json二进制问题)文件名数据:封装为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不限于多部分或x-www-form-urlencoded的POST。Content-TypeHeader与HTTPPOST方法正交(您可以填写适合您的MIME类型)。这也是典型的基于HTML表示的Web应用程序的情况(例如,json有效负载对于传输ajax请求的有效负载变得非常流行)。
关于HTTP上的Restful API,我接触到的最流行的内容类型是application/xml和application/json。
应用程序/xml:
数据大小:XML非常冗长,但在使用压缩时通常不会出现问题,并且认为写访问情况(例如通过POST或PUT)比读访问更罕见(在许多情况下,它小于所有流量的3%)。很少有我必须优化写性能的情况存在非ascii字符:可以使用utf-8作为XML编码二进制数据的存在:需要使用base64编码文件名数据:可以在XML中封装此内部字段
应用程序/json
数据大小:比XML更紧凑,仍然是文本,但可以压缩非ascii字符:json是utf-8二进制数据:base64(另见json二进制问题)文件名数据:封装为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>
我同意曼努埃尔所说的很多话。事实上,他的评论是指这个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的工具支持,以及如何轻松地适应一种上传机制。
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输出中。
在我的案例中,问题是响应contentType是application/x-www-form-urlencoded,但实际上它包含一个JSON作为请求的主体。Django当我们在Django中访问request.data时,它无法正确地转换它,因此访问request.body。
请参考此答案以更好地理解:异常:读取请求的数据流后无法访问正文