We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
这篇文章旨在记录自己解惑过程,比如
Form Data
Request Payload
application/x-www-form-urlencoded
application/json
POST
OPTIONS
话不多说,直接进入主题。
这两个截图就是写这篇文章的初衷,微信文章在打开的时候是显示的 Form Data,第二张图是掘金在打开文章发起的请求,当时看到就特奇怪,Form Data 和 Request Payload 这俩货有啥区别?为啥都是 POST 请求,但却有两种发送数据的方式?
我这个人就是属于碰到这种奇怪的问题不把他搞清楚就睡不了觉的人,我们直接在本地场景重现,好好看看这俩货。
如果不想看中间的分析过程,可以直接点击 总结 看杰伦。
本地起两个服务,前端和后端,通过创建 XMLHttpRequest 对象来进行数据传输,并通过 setRequestHeader() 来改变 Content-Type,最终我们在调试工具中完美重现了两种模式。
XMLHttpRequest
setRequestHeader()
Content-Type
文章里的示例代码都可以从这个仓库里找到,希望自己亲自尝试的小伙伴可以点击查看详情 示例地址。
git clone -b demo/study-post-request https://github.com/jsjzh/tiny-codes.git
如果希望看到 Request Payload,需要设置请求头部 Content-Type: application/json,再将数据经过 JSON.stringify 序列化后发送。
Content-Type: application/json
JSON.stringify
大家可以看到我这里的 Origin: http://localhost.charlesproxy.com:3000,这是因为要用 charles 抓本地包,得用这做一层代理
Origin: http://localhost.charlesproxy.com:3000
直接上抓包的截图
上半部分就是一个完整的 http 请求,空行上面为请求头,空行下面是请求体,可以看到我们的请求体就是一个 json 序列化后的字符串。
下半部分,注意 JSON 和 JSON Text 两个 tab,这个是我们设置了 Content-Type: application/json 了之后,charles 自动会给带上的。
JSON
JSON Text
后端接到 http 请求后,就是截取空行后的这个请求体解析,因为我们传了 Content-Type: application/json,所以后端知道请求体是一个 json 字符串,就可以用 JSON.parse 来解析。
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,数据类型都被正确的传输了。
bar: undefined
number
boolean
null
再来说说 Form Data,我们需要设置 Content-Type: application/x-www-form-urlencoded,再将数据通过 qs.stringify 序列化后再发送。
Content-Type: application/x-www-form-urlencoded
qs.stringify
qs 即为 qs npm source,是一个将数据 querystring 化的库 可以简单理解成他可以把一个对象转换成类似 get 请求中 ? 后面的查询字段 key=data&key2=data2 如果不经过 qs 处理直接发送,方法会使用 toString() 来将数据转为字符串,如果传输的是对象,你会得到 [object Object]
qs 即为 qs npm source,是一个将数据 querystring 化的库
可以简单理解成他可以把一个对象转换成类似 get 请求中 ? 后面的查询字段 key=data&key2=data2
key=data&key2=data2
如果不经过 qs 处理直接发送,方法会使用 toString() 来将数据转为字符串,如果传输的是对象,你会得到 [object Object]
toString()
[object Object]
这里也直接贴出抓包的截图
上半部分就是 http 请求,可以看到当我们设置 Content-Type: application/x-www-form-urlencoded 请求体也是放在了空行之后。
下半部分,对比刚才的 application/json 就能发现不一样的地方了,JSON 和 JSON Text 的 tab 不见了,取而代之的是 Form tab。
Form
后端接到 http 请求之后,也是截取的空行后面的请求体,并使用 qs.parse 进行解析。
qs.parse
{ "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: ""。
foo: null
foo: ""
刚才我们尝试了正确的 Content-Type 对应正确的序列化方式
application/json + JSON.stringify
application/x-www-form-urlencoded + qs.stringify
但其实我们观察到实际的 http 请求,这两个 Content-Type 都是将数据放在空行后传输,所以我们当然也可以交换他们的序列化方式。
这里直接就说结论,我们设置了 application/json,但使用 qs.stringify 序列化,结果就是
在后端我们当然可以手动用 qs.parse 来进行解析,但是我们为什么要给自己埋坑?
同理,使用了 Content-Type 和不正确的序列化方式,不仅 chrome 和 charles 无法解析,后端也会有疑惑,更重要的是会给自己埋坑。
诶,没错,我就想皮一下
前面说了这么多,现在来总结一下
application/x-www-urlencoded
Header
那我们在开发中应该如何选择 Content-Type?建议如果不是项目有特别要求,都使用 application/json,原因有以下几点
qs
query-string
querystring
x-www-form-urlencoded
{ "a": { "b": { "c": { "d": { "e": { "f": { "g": { "name": "king" } } } } } } } }
因为对象嵌套的层数太深,解析后就成了如下
{ "a": { "b": { "c": { "d": { "e": { "[f][g][name]": "king" } } } } } }
当然,使用了 application/json 之后会有些不一样
Options
Access-Control-Request-Headers: Content-Type
当然,再说下去就是 CORS 的知识点了,这方面也有很多内容可以掰开细说,我也正在整理这方面的内容,可以小小的期待一下。
CORS
不知道这篇文章是否给你带来了一些帮助,如果有的话是我的荣幸,在平时碰到问题的时候不妨可以挖的深一点,就像这次的 Form Data 和 Request Payload,当我们挖掘到 http 请求层面就能发现两者其实并无区别,就是浏览器对于 http 协议的一种封装,而正确的使用 Content-Type 就是我们和后端联调的一个约定,也是一个规范。
我们当然可以随意设置 Content-Type,但是这就需要和后端进行非必要联调,并且也不方便后续理解维护,所以我们能简单就简单一些,有些框架会自动根据 Content-Type 的值来解析请求体,头发已经这么少了,我们就不要强行增加游戏难度了。
代码即人生,我甘之如饴。
技术不断在变 头脑一直在线 前端路漫漫 我们下期见 by --- 裤裆三重奏
技术不断在变 头脑一直在线 前端路漫漫 我们下期见
by --- 裤裆三重奏
我在这里 gayhub@jsjzh 欢迎大家来找我玩儿。
欢迎小伙伴们直接加我,拉你进群一起搞事情,记得备注一下你是从哪里看到文章的。
ps: 如果图片失效,可以加我 wechat: kimimi_king
The text was updated successfully, but these errors were encountered:
jsjzh
No branches or pull requests
前言
这篇文章旨在记录自己解惑过程,比如
Form Data
和Request Payload
有什么区别?application/x-www-form-urlencoded
和application/json
有什么区别?开发中我们应该怎么选择?POST
的跨域请求中,有办法不发送OPTIONS
预检请求也能发送数据的方法么?话不多说,直接进入主题。
发现问题,从两个截图开始
这两个截图就是写这篇文章的初衷,微信文章在打开的时候是显示的
Form Data
,第二张图是掘金在打开文章发起的请求,当时看到就特奇怪,Form Data
和Request Payload
这俩货有啥区别?为啥都是POST
请求,但却有两种发送数据的方式?我这个人就是属于碰到这种奇怪的问题不把他搞清楚就睡不了觉的人,我们直接在本地场景重现,好好看看这俩货。
如果不想看中间的分析过程,可以直接点击 总结 看杰伦。
场景重现
本地起两个服务,前端和后端,通过创建
XMLHttpRequest
对象来进行数据传输,并通过setRequestHeader()
来改变Content-Type
,最终我们在调试工具中完美重现了两种模式。文章里的示例代码都可以从这个仓库里找到,希望自己亲自尝试的小伙伴可以点击查看详情 示例地址。
Request Payload
如果希望看到
Request Payload
,需要设置请求头部Content-Type: application/json
,再将数据经过JSON.stringify
序列化后发送。直接上抓包的截图
上半部分就是一个完整的 http 请求,空行上面为请求头,空行下面是请求体,可以看到我们的请求体就是一个 json 序列化后的字符串。
下半部分,注意
JSON
和JSON Text
两个 tab,这个是我们设置了Content-Type: application/json
了之后,charles 自动会给带上的。后端接到 http 请求后,就是截取空行后的这个请求体解析,因为我们传了
Content-Type: application/json
,所以后端知道请求体是一个 json 字符串,就可以用JSON.parse
来解析。发送的数据为
解析的数据为
可以看到除了
bar: undefined
之外,number
、boolean
和null
,数据类型都被正确的传输了。Form Data
再来说说
Form Data
,我们需要设置Content-Type: application/x-www-form-urlencoded
,再将数据通过qs.stringify
序列化后再发送。这里也直接贴出抓包的截图
上半部分就是 http 请求,可以看到当我们设置
Content-Type: application/x-www-form-urlencoded
请求体也是放在了空行之后。下半部分,对比刚才的
application/json
就能发现不一样的地方了,JSON
和JSON Text
的 tab 不见了,取而代之的是Form
tab。后端接到 http 请求之后,也是截取的空行后面的请求体,并使用
qs.parse
进行解析。发送的数据为
解析的数据为
经过和
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
序列化,结果就是Request Payload
无法解析,遂无法格式化数据JSON
和JSON Text
无法解析Content-Type
为application/json
,就会使用JSON.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 个参数(当然,这两个配置都可以修改)举一个例子因为对象嵌套的层数太深,解析后就成了如下
当然,使用了
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
的值来解析请求体,头发已经这么少了,我们就不要强行增加游戏难度了。页脚
代码即人生,我甘之如饴。
我在这里 gayhub@jsjzh 欢迎大家来找我玩儿。
欢迎小伙伴们直接加我,拉你进群一起搞事情,记得备注一下你是从哪里看到文章的。
The text was updated successfully, but these errors were encountered: