前端和后端,都是通过XHRHttpRequest
对象来进行数据传输,并通过setRequestHeader()
来改变Content-Type
。
Request Payload
如果希望看到 Request Payload
,需要设置请求头部 Content-Type: application/json
,再将数据经过 JSON.stringify
序列化后发送。
后端接到 http 请求后,就是截取空行后的这个请求体解析,因为我们传了 Content-Type: application/json,所以后端知道请求体是一个 json 字符串,就可以用 JSON.parse 来解析。
发送的数据:
{
"name": "yuan",
"age": 18,
"isVip": true,
"groups": [1, 2, 3],
"address": "",
"pets": null,
"jobs": undefined,
"extra": { "tel": 123456 }
}
经过JSON.parse解析的数据为:
{
"name": "yuan",
"age": 18,
"isVip": true,
"groups": [1, 2, 3],
"address": "",
"pets": null,
"extra": { "tel": 123456 }
}
除了 jobs: undefined
之外,number
、boolean
和 null
,数据类型都被正确的传输了。
Form Data
再来说说 Form Data
,我们需要设置 Content-Type: application/x-www-form-urlencoded
,再将数据通过 qs.stringify
序列化后再发送。
qs 即为 qs npm source,是一个将数据 querystring 化的库
可以简单理解成他可以把一个对象转换成类似 get 请求中 ? 后面的查询字段 key=data&key2=data2
如果不经过 qs 处理直接发送,方法会使用 toString() 来将数据转为字符串,如果传输的是对象,你会得到 [object Object]
后端接到 http 请求之后,也是截取的空行后面的请求体,并使用 qs.parse
进行解析。
发送的数据:
{
"name": "yuan",
"age": 18,
"isVip": true,
"groups": [1, 2, 3],
"address": "",
"pets": null,
"jobs": undefined,
"extra": { "tel": 123456 }
}
经过JSON.parse解析的数据为:
{
"name": "yuan",
"age": "18",
"isVip": "true",
"groups": ["1", "2", "3"],
"address": "",
"pets": "",
"extra": { "tel": "123456" }
}
经过和Content-Type: application/json
对比,我们可以看到,不仅 number
和 boolean
的数据类型丢失,并且 pets: null
还被转换成了 pets: ""
。
总结:
Form Data
和Request Payload
就是因为请求的Content-Type
不同,而不同的解析请求体后的呈现方式Content-Type
设置成application/json
还是application/x-www-urlencoded
在 http 请求中,除了 Header 以外并无区别,都是将请求体放在空行后
在开发中,如果不是项目有特别要求,都使用 application/json
,原因有以下几点:
- 原生自带的
JSON.stringify
和JSON.parse
不香么?qs 在前端就有很多实现,比如 qs 和query-string
,还有 node 自带的querystring
x-www-form-urlencoded
需要使用配套qs.stringify
,后端解析数据后会丢失数据类型,比如number
、boolean
、null
- 不同的框架对于
qs.parse
的实现方式不同,在项目刚开始对接时可能会有前后端对齐解析方式的操作 - 前端的 qs 仓库默认只能处理 5 层对象,默认只能解析 1000 个参数(当然,这两个配置都可以修改)举一个例子
使用了 application/json
之后会有些不一样:
- 配置头部
Content-Type: application/json
之后就不是简单请求,会发起一个Options
预检请求 - 后端需要同步配置
Access-Control-Request-Headers: Content-Type
,允许前端配置Content-Type
头部