共计 4098 个字符,预计需要花费 11 分钟才能阅读完成。
前言
这篇文章旨在记录本人解惑过程,比方
- 在 chrome 调试工具中,
Form Data
和Request Payload
有什么区别? application/x-www-form-urlencoded
和application/json
有什么区别?开发中咱们应该怎么抉择?- 为什么后端有时会无奈解析本人发送的数据?
- 在
POST
的跨域申请中,有方法不发送OPTIONS
预检申请也能发送数据的办法么?
话不多说,间接进入主题。
发现问题,从两个截图开始
这两个截图就是写这篇文章的初衷,微信文章在关上的时候是显示的 Form Data
,第二张图是掘金在关上文章发动的申请,过后看到就特奇怪,Form Data
和 Request Payload
这俩货有啥区别?为啥都是 POST
申请,但却有两种发送数据的形式?
我这个人就是属于碰到这种奇怪的问题不把他搞清楚就睡不了觉的人,咱们间接在本地场景重现,好好看看这俩货。
如果不想看两头的剖析过程,能够间接点击 总结 看杰伦。
场景重现
本地起两个服务,前端和后端,通过创立 XMLHttpRequest
对象来进行数据传输,并通过 setRequestHeader()
来扭转 Content-Type
,最终咱们在调试工具中完满重现了两种模式。
文章里的示例代码都能够从这个仓库里找到,心愿本人亲自尝试的小伙伴能够点击查看详情 示例地址。
git clone -b demo/study-post-request https://github.com/jsjzh/tiny-codes.git
Request Payload
如果心愿看到 Request Payload
,须要设置申请头部 Content-Type: application/json
,再将数据通过 JSON.stringify
序列化后发送。
大家能够看到我这里的
Origin: http://localhost.charlesproxy.com:3000
,这是因为要用 charles 抓本地包,得用这做一层代理
间接上抓包的截图
上半局部就是一个残缺的 http 申请,空行下面为申请头,空行上面是申请体,能够看到咱们的申请体就是一个 json 序列化后的字符串。
下半局部,留神 JSON
和 JSON Text
两个 tab,这个是咱们设置了 Content-Type: application/json
了之后,charles 主动会给带上的。
后端接到 http 申请后,就是截取空行后的这个申请体解析,因为咱们传了 Content-Type: application/json
,所以后端晓得申请体是一个 json 字符串,就能够用 JSON.parse
来解析。
发送的数据为
{
"name": "king",
"age": 18,
"isAdmain": true,
"groups": [1, 2, 3],
"address": "","foo": null,"bar": undefined,"extra": {"wechat":"kimimi_king","qq": 454075623}
}
解析的数据为
{
"name": "king",
"age": 18,
"isAdmain": true,
"groups": [1, 2, 3],
"address": "","foo": null,"extra": {"wechat":"kimimi_king","qq": 454075623}
}
能够看到除了 bar: 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 申请,能够看到当咱们设置 Content-Type: application/x-www-form-urlencoded
申请体也是放在了空行之后。
下半局部,比照方才的 application/json
就能发现不一样的中央了,JSON
和 JSON Text
的 tab 不见了,取而代之的是 Form
tab。
后端接到 http 申请之后,也是截取的空行前面的申请体,并应用 qs.parse
进行解析。
发送的数据为
{
"name": "king",
"age": 18,
"isAdmain": true,
"groups": [1, 2, 3],
"address": "","foo": null,"bar": undefined,"extra": {"wechat":"kimimi_king","qq": 454075623}
}
解析的数据为
{
"name": "king",
"age": "18",
"isAdmain": "true",
"groups": ["1", "2", "3"],
"address": "","foo":"",
"extra": {"wechat": "kimimi_king", "qq": "454075623"}
}
通过和 Content-Type: application/json
比照,咱们能够看到,不仅 number
和 boolean
的数据类型失落,并且 foo: null
还被转换成了 foo: ""
。
替换序列化形式
方才咱们尝试了正确的 Content-Type
对应正确的序列化形式
application/json
+ JSON.stringify
application/x-www-form-urlencoded
+ qs.stringify
但其实咱们察看到理论的 http 申请,这两个 Content-Type
都是将数据放在空行后传输,所以咱们当然也能够替换他们的序列化形式。
application/json + qs.stringify
这里间接就说论断,咱们设置了 application/json
,但应用 qs.stringify
序列化,后果就是
- chrome 调试工具的
Request Payload
无奈解析,遂无奈格式化数据 - charles 工具的
JSON
和JSON Text
无奈解析 - 最重要的,后端若是读取了
Content-Type
为application/json
,就会应用JSON.parse
来解析数据
在后端咱们当然能够手动用
qs.parse
来进行解析,然而咱们为什么要给本人埋坑?
application/x-www-form-urlencoded + JSON.stringify
同理,应用了 Content-Type
和不正确的序列化形式,不仅 chrome 和 charles 无奈解析,后端也会有纳闷,更重要的是会给本人埋坑。
总结
诶,没错,我就想皮一下
后面说了这么多,当初来总结一下
Form Data
和Request Payload
就是因为申请的Content-Type
不同,而不同的解析申请体后的出现形式Content-Type
设置成application/json
还是application/x-www-urlencoded
在 http 申请中,除了Header
以外并无区别,都是将申请体放在空行后
那咱们在开发中应该如何抉择 Content-Type
?倡议如果不是我的项目有特地要求,都应用 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 个参数(当然,这两个配置都能够批改)举一个例子
{
"a": {
"b": {
"c": {
"d": {
"e": {
"f": {"g": { "name": "king"}
}
}
}
}
}
}
}
因为对象嵌套的层数太深,解析后就成了如下
{
"a": {
"b": {
"c": {
"d": {
"e": {"[f][g][name]": "king"
}
}
}
}
}
}
当然,应用了 application/json
之后会有些不一样
- 配置头部
Content-Type: application/json
之后就不是简略申请,会发动一个Options
预检申请 - 后端须要同步配置
Access-Control-Request-Headers: Content-Type
,容许前端配置Content-Type
头部
当然,再说下去就是 CORS
的知识点了,这方面也有很多内容能够掰开细说,我也正在整顿这方面的内容,能够小小的期待一下。
后语
不晓得这篇文章是否给你带来了一些帮忙,如果有的话是我的荣幸,在平时碰到问题的时候无妨能够挖的深一点,就像这次的 Form Data
和 Request Payload
,当咱们开掘到 http 申请层面就能发现两者其实并无区别,就是浏览器对于 http 协定的一种封装,而正确的应用 Content-Type
就是咱们和后端联调的一个约定,也是一个标准。
咱们当然能够随便设置 Content-Type
,然而这就须要和后端进行非必要联调,并且也不不便后续了解保护,所以咱们能简略就简略一些,有些框架会主动依据 Content-Type
的值来解析申请体,头发曾经这么少了,咱们就不要强行减少游戏难度了。
页脚
代码即人生,我甘之如饴。
技术一直在变
头脑始终在线
前端路漫漫
咱们下期见by — 裤裆三重奏
我在这里 gayhub@jsjzh 欢送大家来找我玩儿。
欢送小伙伴们间接加我,拉你进群一起搞事件,记得备注一下你是从哪里看到文章的。
ps: 如果图片生效,能够加我 wechat: kimimi_king