<span hidden="" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
<meta itemprop="name" content="LiYang">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden="" itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Leon's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
POST提交数据的几种方式
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2019-03-30T15:57:49+08:00">
2019-03-30
</time>
</span>
<span class="post-meta-divider">|</span>
<span class="page-pv">本文总阅读量
<span class="busuanzi-value" id="busuanzi_value_page_pv">516</span>次
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>提到HTTP使用<code>POST</code>请求提交数据的方式,第一反应就是以对象形式提交,如果是文件,则换成<code>formData</code>形式提交。除此之外好像还有个一长串的<code>x-www-form-urlencoded</code>,可这全是碎片化的知识点,毫无章法。最近做项目时用到了<code>axios</code>,详细的翻看了一下文档,有个地方引起了我的兴趣:<code>By default, axios serializes JavaScript objects to JSON. To send data in the application/x-www-form-urlencoded format instead, you can use one of the following options</code>。等等,这里说<code>axios</code>默认使用序列化的<code>JSON</code>发送数据,但如果你要使用<code>application/x-www-form-urlencoded</code>格式的话,可以使用<code>qs</code>库或者<code>new URLSearchParams()</code>。赶紧网上查阅资料,才发现自己遇到了知识盲点,现系统的总结一下这个小知识点。</p>
概述
常用的HTTP请求方法有GET
、POST
、PUT
、DELETE
、HEAD
、OPTIONS
等。客户端发送一个HTTP请求到服务器,这个请求消息包括:请求行、请求头部、空行和请求数据:
1 2 3 4
| <method> <request-url> <version> <headers>
<entity-body>
|
我们使用POST
发送的数据必须放在消息主体(entity-body)中,但是HTTP协议并没有规定使用什么样的编码方式来传送数据。这个编码方式前端可以自己来决定,但是数据发出去,还要服务端能解析成功才有意义。服务端自有不同的方法来解析不同编码格式的数据,而且服务端可以根据请求头(headers)内的Content-Type
字段去获知编码方式,然后去解析数据。
几种常见的Content-Type类型
application/x-www-form-urlencoded
是一种比较常见的编码方式。我们在使用浏览器原生的form
表单提交数据时,如果没有指定enctype
属性,则默认会以该方式提交数据。
1 2 3 4 5
| <form action="http://www.baidu.com" method="post"> <input type="text" name="name"> <input type="text" name="age"> <button type="submit">提交</button> </form>
|
在浏览里可以看到:
首先,我们看到请求头Request Headers
内的Content-Type
字段被设置为了application/x-www-form-urlencoded
;其次,提交的数据按照key1=value1&key2=value2
的方式进行了编码,服务端语言对这种编码方式有很好的支持。
当我们在上传图片或者文件的时候,会用到这种编码方式。如果使用原生form
表单,则需要将enctype
设置为multipart/form-data
进行提交。如果使用ajax
库,则需要以formData
的形式提交数据:
1 2 3 4 5 6 7
| let _formData = new FormData(); _formData.append('file', file); _formData.append('suffix', suffix); _formData.append('perpetual', true); _formData.append('applicationId', applicationId); _formData.append('partyId', partyId); api.saveHeadImg(_formData).then(console.log(res));
|
在浏览器中:
首先,请求头内Content-Type
除了有multipart/form-data;
外,额外生成了一个boundary
字段,这个字段用来分割请求数据内的不同字段。在请求体内,数据字段每部分都是以--boundary
开头,紧接着是内容描述信息,包括字段名称,字段值等。请求体最后以--boundary—
标识结束。
application/json
以application/json
作为响应头或者请求头都非常常见。这种编码方式会告诉服务端提交的数据类型是序列化后的JSON
字符串,前端在调接口提交数据时,直接以对象形式传递数据即可。
1 2 3 4 5
| let params = { loanApplyNo: 'XTFQ201903270010', returnUrl: window.location.origin + '/loan/result', }; api.saveData(params).then(console.log(res));
|
在浏览器端:
axios
这个库默认就是使用序列化的JSON
形式POST
数据。但如果服务器端比较蛋疼的规定只接收application/x-www-form-urlencoded
格式,则可以使用qs
库或者URLSearchParams API来做转换处理:
1 2 3 4 5 6 7 8
| const params = new URLSearchParams(); params.append('param1', 'value1'); params.append('param2', 'value2'); axios.post('/foo', params);
import qs from 'qs'; axios.post('/foo', qs.stringify({ 'bar': 123 }));
|
写到这里,想到Angular4+
同样提供了 HttpParams 类来将普通对象转换为application/x-www-form-urlencoded
。当时写Angular
的时候还没意识到这一点,真是糊涂。
以上就是平时用到最多的三种POST
数据类型,当然还有其他的编码格式,但是我暂时还未用到或者见到过。日后使用到其他格式类型,可以继续补充。
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-tags">
<a href="/tags/随写/" rel="tag"># 随写</a>
<a href="/tags/HTTP/" rel="tag"># HTTP</a>
</div>
<div class="post-nav">
<div class="post-nav-next post-nav-item">
<a href="/2019/03/16/RxJS学习总结-Subject/" rel="next" title="RxJS学习总结-Subject">
<i class="fa fa-chevron-left"></i> RxJS学习总结-Subject
</a>
</div>
<span class="post-nav-divider"></span>
<div class="post-nav-prev post-nav-item">
<a href="/2019/04/13/React源码学习-Fiber遍历/" rel="prev" title="React源码学习-Fiber遍历">
React源码学习-Fiber遍历 <i class="fa fa-chevron-right"></i>
</a>
</div>
</div>
</footer>
本文所讲的 POST 请求是 HTTP/1.1 协议中规定的众多 HTTP 请求方法的其中最常用的一个。一般使用 POST 请求方法向服务器发送数据(主要是一些创建更新操作),本文讨论的是 POST 请求方法常用的四种数据提交格式。
由于 HTTP/1.1 协议中并没有对请求使用什么编码方式进行规定,所以理论上开发者完全可以自己决定请求的 Body 体使用什么格式,当然实际上大家都还是用通用的那么几种编码方式来提交数据(生态很关键)。
注:以下排名不分先后。。。
application/x-www-form-urlencoded
对于浏览器原生的 form 表单,enctype 的值不指定的话,默认就是这个家伙。实际上大部分情况都使用它即可,编码方式足够简单高效,各方面支持也都很完备,如各大浏览器调试工具、各大抓包软件等。
key1=val1&key2=val2" title="" data-original-title=“复制”>
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
key1=val1&key2=val2
基本的请求类似上面这样,数据的编码方式采用 key1=val1&key2=val2 的形式,对其中的键值对都需要使用 URL Encode 编码一下。其实就是和 GET 请求的数据提交格式是一样的,只不过位置从 Request URL 上换到了 Request Body 里。
这种格式结构简单,但对于数据层级较深的情况,比如一些有复杂层级关系的接口数据,这种方式就显得有点力不从心了。另一方面,对于需要上传二进制数据(比如图像、音频等文件),这种方式就不那么高效了,而且对于非 ASCII 码的数据就丢失了,所以传文件的情况就不能使用这种方式。
适用场景:数据量不大、数据层级不深的情况下强烈建议这种数据提交格式。
multipart/form-data
当你需要提交文件、非 ASCII 码的数据或者是二进制流数据,则使用这种提交方式。类似下面这个请求示例:
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
demo
------WebKitFormBoundaryPAlLG7hJKNYc4ft3
Content-Disposition: form-data; name="file"; filename="demo.png"
Content-Type: image/png
------WebKitFormBoundaryPAlLG7hJKNYc4ft3–" title="" data-original-title=“复制”>
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryPAlLG7hJKNYc4ft3
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name=“text”
demo
------WebKitFormBoundaryPAlLG7hJKNYc4ft3
Content-Disposition: form-data; name=“file”; filename=“demo.png”
Content-Type: image/png
------WebKitFormBoundaryPAlLG7hJKNYc4ft3–
第二行指定编码方式 Content-Type 为 multipart/form-data,紧接着生成一个分界线 boundary 即 ----WebKitFormBoundaryPAlLG7hJKNYc4ft3
,又臭又长的目的是为了避免和 Body 正文内容有冲突,它的作用是用来分隔不同的字段。
Body 体分为多个结构类似的部分,每一部分以 --boundary
开头,因为本次请求生成的 boundary 为 ----WebKitFormBoundaryPAlLG7hJKNYc4ft3
,所以最终是 ------WebKitFormBoundaryPAlLG7hJKNYc4ft3
。接着是描述内容的元信息,包括字段名称,如果是文件则还有文件名称和文件类型。接着留一空行,然后才是字段值。什么时候结束呢,以 --boundary--
标志结束。
这种方式本就是专为上传文件的场景设计的,虽然你也可以使用这种方式传递普通数据,但无疑会增加不少数据包的大小(这么多 boundary 还是有不少空间占用的)。
适用场景:文件上传。
application/json
很明显在 JSON 格式火之前,肯定没有它的,前面说到使用什么提交数据方式是没有硬性规定的,所以在 JSON 格式火了以后,尤其以其优秀的数据结构表达能力,逐渐流行开来,现在我们对它完全不会陌生。
{“name”:“xfly”,“age”: 24, “hobby”:[“x”,“xx”,“xxx”]}" title="" data-original-title=“复制”>
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{“name”:“xfly”,“age”: 24, “hobby”:[“x”,“xx”,“xxx”]}
适用场景:数据结构较复杂,层级较深的情况。