首页 文章

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

提问于
浏览
1127

在HTTP中有两种POST数据的方法: application/x-www-form-urlencodedmultipart/form-data . 据我所知,如果使用 multipart/form-data ,大多数浏览器只能上传文件 . 在API上下文中使用其中一种编码类型时是否有任何其他指导(不涉及浏览器)?这可能是例如根据:

  • 数据大小

  • 存在非ASCII字符

  • 存在于(未编码的)二进制数据上

  • 需要传输额外的数据(如文件名)

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

6 回答

  • 84

    TL;DR

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


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

    对于 application/x-www-form-urlencoded ,发送到服务器的HTTP消息的主体实际上是一个巨大的查询字符串 - 名称/值对由&符号( & )分隔,名称通过等号( = )与值分隔 . 一个例子是:

    MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

    根据specification

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

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

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

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

  • 1751

    从我这边稍微提示上传HTML5画布图像数据:

    我正在开发一个打印店的项目,并且由于将图像上传到来自HTML5 canvas 元素的服务器而出现了一些问题 . 我挣扎了至少一个小时,我没有得到它在我的服务器上正确保存图像 .

    一旦我将jQuery ajax调用的 contentType 选项设置为 application/x-www-form-urlencoded ,一切都以正确的方式进行,并且base64编码的数据被正确解释并成功保存为图像 .


    也许这有助于某人!

  • 28

    如果你需要使用Content-Type = x-www-urlencoded-form,那么不要使用FormDataCollection作为参数:在asp.net Core 2中,FormDataCollection没有Formatters所需的默认构造函数 . 改为使用IFormCollection:

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

    我认为HTTP不限于多部分或x-www-form-urlencoded中的POST . Content-Type Header与HTTP POST方法正交(您可以填写适合您的MIME类型) . 对于典型的基于HTML表示的webapps也是如此(例如,json有效载荷变得非常流行以传输ajax请求的有效载荷) .

    关于HTTP上的Restful API,我接触过的最流行的内容类型是application / xml和application / json .

    application / xml:

    • data-size:XML非常冗长,但在使用压缩时通常不会出现问题,并认为写访问案例(例如通过POST或PUT)作为读访问更为罕见(在许多情况下,它仅占总数的3%)交通) . 很少有我必须优化写入性能的情况

    • 存在非ascii字符:您可以使用utf-8作为XML编码

    • 存在二进制数据:需要使用base64编码

    • filename数据:您可以在XML中封装此内部字段

    application / json

    • data-size:比XML更紧凑,仍然是文本,但你可以压缩

    • non-ascii chars:json是utf-8

    • 二进制数据:base64(另见json-binary-question

    • filename数据:在json中封装为自己的field-section

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

    我会尝试表示二进制数据作为自己的资产/资源 . 它增加了另一个电话,但更好地解耦了 . 示例图片:

    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>
    
  • 1

    我同意曼努埃尔的说法 . 事实上,他的评论指的是这个网址......

    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用户努力上传文件,那么他们就会离开,您将花费大量时间来支持他们 .

    除此之外,您将拥有编写API的工具支持,以及容纳一个上传机制与另一个上载机制的容易程度 .

  • 117

    READ AT LEAST THE FIRST PARA HERE!

    我知道这已经晚了3年,但马特(接受)的答案是不完整的,最终会让你陷入困境 . 这里的关键是,如果您选择使用 multipart/form-data ,则边界不得出现在服务器最终接收的文件数据中 .

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

    multipart/form-data 的问题是文件数据中不能出现边界分隔符(参见RFC2388; 5.2节还包含一个相当蹩脚的借口,因为没有适当的聚合MIME类型可以避免这个问题) .

    所以,乍一看, multipart/form-data 在任何文件上传,二进制或其他方面都没有任何 Value . 如果你没有正确选择你的边界,那么你最终会遇到问题,无论你是发送纯文本还是原始二进制文件 - 服务器会在错误的地方找到边界,你的文件会被截断,或者POST将失败 .

    关键是选择编码和边界,使选定的边界字符不会出现在编码输出中 . 一个简单的解决方案是使用 base64 (不要使用原始二进制文件) . 在base64中,3个任意字节被编码为4个7位字符,其中输出字符集为 [A-Za-z0-9+/=] (即字母数字,或'+','/','=') . = 是一种特殊情况,可能只出现在编码输出的末尾,如单个 = 或双 == . 现在,选择您的边界作为7位ASCII字符串,该字符串不能出现在 base64 输出中 . 您在网上看到的许多选择都无法通过此测试 - 例如,MDN形式docs,在发送二进制数据时使用"blob"作为边界 - 不好 . 但是,"!blob!"之类的内容永远不会出现在 base64 输出中 .

相关问题