HTTP 冷知识 | HTTP 请求中,空格应该被编码为 %20 还是 + ?
HTTP 请求中,空格应该被编码为什么?今天我们走进 RFC 文档和 W3C 文档,了解一下这个「史诗级」大坑。
1.%20 还是 + ?
开始讲解前先看个小测试,在浏览器里输入 blank test( blank 和 test 间有个空格),我们看看浏览器如何处理的:
从动图可以看出浏览器把空格解析为一个加号「+」。
是不是感觉有些奇怪?我们再做个测试,用浏览器提供的几个函数试一下:
encodeURIComponent("blank test") // "blank%20test"
encodeURI("q=blank test") // "q=blank%20test"
new URLSearchParams("q=blank test").toString() // "q=blank+test"
代码是不会说谎的,其实上面的结果都是正确的,encode 结果不一样,是因为 URI 规范和 W3C 规范冲突了,才会搞出这种让人疑惑的乌龙事件。
2.冲突的协议
我们首先看看 URI 中的保留字,这些保留字不参与编码。保留字符一共有两大类:
gen-delims: :/?#[]@sub-delims: !$&'()*+,;=
URI 的编码规则也很简单,先把非限定范围的字符转为 16 进制,然后前面加百分号。
空格这种不安全字符转为十六进制就是 0x20,前面再加上百分号 % 就是 %20:
所以这时候再看 encodeURIComponent 和 encodeURI 的编码结果,就是完全正确的。
既然空格转为%20 是正确的,那转为 + 是怎么回事?这时候我们就要了解一下 HTML form 表单的历史。
早期的网页没有 AJAX 的时候,提交数据都是通过 HTML 的 form 表单。form 表单的提交方法可以用 GET 也可以用 POST,大家可以在 MDN form 词条上测试:
经过测试我们可以看出表单提交的内容中,空格都是转为加号的,这种编码类型就是 application/x-www-form-urlencoded,在 WHATWG 规范里是这样定义的:
到这里基本上就破案了,URLSearchParams 做 encode 的时候,就按这个规范来的。我找到了 URLSearchParams 的 Polyfill 代码,里面就做了 %20 到 + 的映射:
replace = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+', // <= 就是这个
'%00': '\x00'
}
规范里对这个编码类型还有解释说明:
The
application/x-www-form-urlencodedformat is in many ways an aberrant monstrosity, the result of many years of implementation accidents and compromises leading to a set of requirements necessary for interoperability, but in no way representing good design practices. In particular, readers are cautioned to pay close attention to the twisted details involving repeated (and in some cases nested) conversions between character encodings and byte sequences. Unfortunately the format is in widespread use due to the prevalence of HTML forms.这种编码方式就不是个好的设计,不幸的是随着 HTML form 表单的普及,这种格式已经推广开了
其实上面一大段句话就是一个意思:这玩意儿设计的就是 ,但积重难返,大家还是忍一下吧
3.一句话总结
URI 规范里,空格 encode 为
%20,application/x-www-form-urlencoded格式里,空格 encode 为+实际业务开发时,最好使用业内成熟的 HTTP 请求库封装请求,这些杂活儿累活儿框架都干了;
如果非要使用原生 AJAX 提交
application/x-www-form-urlencoded格式的数据,不要手动拼接参数,要用URLSearchParams处理数据,这样可以避免各种恶心的编码冲突。
4.文章推荐
本文选自我的长文HTTP 规范中的那些暗坑,想要了解更多可点击原文查看。
更多推荐:
webpack 中最易混淆的 5 个知识点,掘金快 800 赞了 webpack dll 是个什么东西,看完就能摆脱繁琐的 dll 配置 React Native 性能优化指南从渲染层的角度分析了 RN 性能优化的 6 个点,并以图文形式讲解了 FlatList 的实现原理 Web Scraper——轻量数据爬取利器 介绍了一个小巧的浏览器爬虫插件,可以实现简单的数据爬取功能
最后推荐一下我的个人公众号:「卤蛋实验室」,平时会分享一些前端技术和数据分析的内容,大家感兴趣的话可以关注一波:
HTTP 冷知识 | HTTP 请求中,空格应该被编码为 %20 还是 + ?的更多相关文章
- (转)HTTP请求中URL地址的编码和解码
HTTP请求中,类似 http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fv3Jump.html 的地址 如何解码成 http://www ...
- 有趣的冷知识:编程中Foo, Bar 到底什么意思?
转自:编程中Foo, Bar 到底什么意思? 1 前言 在很多国外计算机书本和一些第三份开源软件的Demo中经常用到两个英文单词Foo,Bar.这到底是什么意思呢?从步入屌丝界的IT生活见到这两个单词 ...
- python冷知识(续)
python 冷知识 1.交互式中修改最大递归深度 大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出. 那到底,默认递归次数限制是多少呢? 可以使用sys这个库来查看 >>&g ...
- URL中的空格字符如何编码
URL encoding the space character: + or %20? 简单理解: ‘?’前的路径中的空格必须为’20%’ ‘?’后的参数中空格可以被编码成’+’(正常情况),然而有时 ...
- 【原创】http请求中加号被替换为空格?源码背后的秘密
这是why技术的第**20**篇原创文章
上一篇文章分享了 Python中的那些冷知识,地址在这里 盘点 Python 中的那些冷知识(一) 今天将接着分享!! 06. 默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参 ...
- requests(二): json请求中固定键名顺序&消除键和值之间的空格
继上一篇requests发送json请求的文章后,实际工作中遇到了以下2种情况. 1:服务端要求json字符串,键名的顺序固定 2.服务端对于接收到的json数据中,若key和value之间有空格, ...
- Python 浮点数的冷知识
本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家. 它提到的部分问题,读者们可以先思考下: 若两个元组相等,即 a==b 且 a is b ...
- Http请求中的Content-Type
转载至:https://segmentfault.com/a/1190000013056786?utm_source=tag-newest 一 前言 ----现在搞前端的不学好http有关的知识已经不 ...
随机推荐
- github使用命令
创建本地库,提交,和绑定github ,上传代码 git init git add README.md git commit -m "first commit" git remot ...
- 组合模式(c++实现)
组合模式 目录 组合模式 定义 动机 UML类图 场景拆解 源码实现 优点 缺点 定义 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式是的用户对单个对象和组合对象的使 ...
- failed parsing overlays.
clearn + rebuild + 重新运行: 删掉模拟器进程 + 重新运行:
- (2)通信中为什么要进行AMC?
AMC,Adaptive Modulation and Coding,自适应调制与编码. 通信信号的传输环境是变化不定的,信道环境时好时差.在这种情景下,我们不可能按照固定的MCS进行信号发送.假如信 ...
- transform-translate3d
translate3d 开启硬件加速,做动效效率比 position 定位置后,改变位置,效果好,比如下拉背景放大效果,上滑的时候背景跟着上滑,可以用 translate3d.亲测效果更好,记下来
- springData表关系:一对多
一.编写实体类进行表关联 1.在一张表的关联属性上添加@OneToMany注解(关联属性用来记录多的一方的信息,是个集合,一般用set) 2.在另一个实体类的关联属性上添加@ManyToOne注解和 ...
- linux --开机自动挂载硬盘【转】
转:http://c.biancheng.net/view/900.html 了解了 mount 命令之后,读者可能会问,系统如何在开机时自动挂载硬盘,它又是怎么知道哪些分区是需要挂载的呢? 很简单, ...
- ql的python学习之路-day9
前言:本节主要学习装饰器 一.装饰器 定义:本质上是个函数,用来装饰其他函数:(就是为其他函数添加附加功能) 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 以上两点可以总 ...
- 枚举:Enum-Int-String之间的转换与扩展
示例枚举: public enum Color { [Description("红色")] Red, [Description("绿色")] Green = 7 ...
- 「雕爷学编程」Arduino动手做(39)——DS18B20温度传感器
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...