POST提交数据的几种方式

提到HTTP使用POST请求提交数据的方式,第一反应就是以对象形式提交,如果是文件,则换成formData形式提交。除此之外好像还有个一长串的x-www-form-urlencoded,可这全是碎片化的知识点,毫无章法。最近做项目时用到了axios,详细的翻看了一下文档,有个地方引起了我的兴趣: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。等等,这里说axios默认使用序列化的JSON发送数据,但如果你要使用application/x-www-form-urlencoded格式的话,可以使用qs库或者new URLSearchParams()。赶紧网上查阅资料,才发现自己遇到了知识盲点,现系统的总结一下这个小知识点。

概述

常用的HTTP请求方法有GETPOSTPUTDELETEHEADOPTIONS等。客户端发送一个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

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的方式进行了编码,服务端语言对这种编码方式有很好的支持。

multipart/form-data

当我们在上传图片或者文件的时候,会用到这种编码方式。如果使用原生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数据类型,当然还有其他的编码格式,但是我暂时还未用到或者见到过。日后使用到其他格式类型,可以继续补充。