HTTP 请求中,空格应该被编码为什么?今天我们走进 RFC 文档W3C 文档,了解一下这个「史诗级」大坑。

1.%20 还是 +

开始讲解前先看个小测试,在浏览器里输入 blank testblanktest 间有个空格),我们看看浏览器如何处理的:

从动图可以看出浏览器把空格解析为一个加号「+」。

是不是感觉有些奇怪?我们再做个测试,用浏览器提供的几个函数试一下:

encodeURIComponent("blank test") // "blank%20test"
encodeURI("q=blank test")        // "q=blank%20test"
new URLSearchParams("q=blank test").toString() // "q=blank+test"

image-20200524184735653

代码是不会说谎的,其实上面的结果都是正确的,encode 结果不一样,是因为 URI 规范W3C 规范冲突了,才会搞出这种让人疑惑的乌龙事件。

2.冲突的协议

我们首先看看 URI 中的保留字,这些保留字不参与编码。保留字符一共有两大类:

  • gen-delims:: / ? # [ ] @
  • sub-delims:! $ & ' ( ) * + , ; =

URI 的编码规则也很简单,先把非限定范围的字符转为 16 进制,然后前面加百分号。

空格这种不安全字符转为十六进制就是 0x20,前面再加上百分号 % 就是 %20


image-20200524184601512

所以这时候再看 encodeURIComponentencodeURI 的编码结果,就是完全正确的。

既然空格转为%20 是正确的,那转为 + 是怎么回事?这时候我们就要了解一下 HTML form 表单的历史。

早期的网页没有 AJAX 的时候,提交数据都是通过 HTML 的 form 表单。form 表单的提交方法可以用 GET 也可以用 POST,大家可以在 MDN form 词条上测试:

经过测试我们可以看出表单提交的内容中,空格都是转为加号的,这种编码类型就是 application/x-www-form-urlencoded,在 WHATWG 规范里是这样定义的:


image-20200524185912590

到这里基本上就破案了,URLSearchParams 做 encode 的时候,就按这个规范来的。我找到了 URLSearchParamsPolyfill 代码,里面就做了 %20+ 的映射:

replace = {
    '!': '%21',
    "'": '%27',
    '(': '%28',
    ')': '%29',
    '~': '%7E',
    '%20': '+', // <= 就是这个
    '%00': '\x00'
}

规范里对这个编码类型还有解释说明:

The application/x-www-form-urlencoded format 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 为 %20application/x-www-form-urlencoded 格式里,空格 encode 为 +

  • 实际业务开发时,最好使用业内成熟的 HTTP 请求库封装请求,这些杂活儿累活儿框架都干了;

  • 如果非要使用原生 AJAX 提交 application/x-www-form-urlencoded 格式的数据,不要手动拼接参数,要用 URLSearchParams 处理数据,这样可以避免各种恶心的编码冲突。

4.文章推荐

本文选自我的长文HTTP 规范中的那些暗坑,想要了解更多可点击原文查看。

更多推荐:


最后推荐一下我的个人公众号:「卤蛋实验室」,平时会分享一些前端技术和数据分析的内容,大家感兴趣的话可以关注一波:

HTTP 冷知识 | HTTP 请求中,空格应该被编码为 %20 还是 + ?的更多相关文章

  1. (转)HTTP请求中URL地址的编码和解码

    HTTP请求中,类似   http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fv3Jump.html  的地址 如何解码成    http://www ...

  2. 有趣的冷知识:编程中Foo, Bar 到底什么意思?

    转自:编程中Foo, Bar 到底什么意思? 1 前言 在很多国外计算机书本和一些第三份开源软件的Demo中经常用到两个英文单词Foo,Bar.这到底是什么意思呢?从步入屌丝界的IT生活见到这两个单词 ...

  3. python冷知识(续)

    python 冷知识 1.交互式中修改最大递归深度 大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出. 那到底,默认递归次数限制是多少呢? 可以使用sys这个库来查看 >>&g ...

  4. URL中的空格字符如何编码

    URL encoding the space character: + or %20? 简单理解: ‘?’前的路径中的空格必须为’20%’ ‘?’后的参数中空格可以被编码成’+’(正常情况),然而有时 ...

  5. 【原创】http请求中加号被替换为空格?源码背后的秘密

    这是why技术的第**20**篇原创文章![在这里插入图片描述](https://user-gold-cdn.xitu.io/2019/12/30/16f550eb82e10eff?w=900& ...

  6. 盘点 Python 中的那些冷知识(二)

    上一篇文章分享了 Python中的那些冷知识,地址在这里 盘点 Python 中的那些冷知识(一) 今天将接着分享!! 06. 默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参 ...

  7. requests(二): json请求中固定键名顺序&消除键和值之间的空格

    继上一篇requests发送json请求的文章后,实际工作中遇到了以下2种情况. 1:服务端要求json字符串,键名的顺序固定  2.服务端对于接收到的json数据中,若key和value之间有空格, ...

  8. Python 浮点数的冷知识

    本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家. 它提到的部分问题,读者们可以先思考下: 若两个元组相等,即 a==b 且 a is b ...

  9. Http请求中的Content-Type

    转载至:https://segmentfault.com/a/1190000013056786?utm_source=tag-newest 一 前言 ----现在搞前端的不学好http有关的知识已经不 ...

随机推荐

  1. 无回显命令执行TIPS

    DNSlog 出属于, POST DATA   HEX  等一些 命令延迟注入 找WEB PATH 将id,pwd,hostname的结果写在js/test1.txt中,命令find . -type  ...

  2. spring的aop的注解配置

    一.使用注解的方式配置后置通知 第一步,创建通知类LogAdvice 第二步,要在spring主配置文件中声明以注解的方式配置spring aop  第三步,测试 二.其他异常配置 package c ...

  3. NetCore项目实战篇05---添加Ocelot网关并集成identity server4认证

    今天来给我们的项目增加API网关,使用Ocelot. 它是系统暴露在外部的一个访问入口,这个有点像代理访问的家伙,就像一个公司的门卫承担着寻址.限制进入.安全检查.位置引导.等等功能.同时我们还要在网 ...

  4. mercurial 入门

    安装 需要python的docutils,故 sudo pip3 install docutils 然后直接安装mercurial sudo pip3 install mercurial 如果超时,则 ...

  5. 从浅入深——理解JSONP的实现原理

    由于浏览器的安全性限制,不允许AJAX访问 协议不同.域名不同.端口号不同的 数据接口,浏览器认为这种访问不安全: 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口 ...

  6. promise的理解和使用

    1. Promise是什么 1.1 promise 的理解 1. 抽象表达: Promise 是 JS 中进行异步编程的新的解决方案(旧的是纯回调形式) 2. 具体表达: (1)从语法上说:Promi ...

  7. 萌新学习SpringMVC

    前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 这篇SpringMVC被催了很久了,这阵子由于做 ...

  8. 【雕爷学编程】Arduino动手做(55)--DHT11温湿度传感器

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践(动手试试)出真知的理念,以学习和交流为目的,这里准备 ...

  9. Angular和Ionic的路由跳转

    一.Angular和Ionic的路由跳转 Angular的路由跳转: constructor(private router:Router){    } .... this.router.navigat ...

  10. DPDK Timer Library原理(学习笔记)

    0 前置知识学习跳表(SkipList) 跳表应具有以下特征: 1)一个跳表应该有多个层(level)组成,通常是10-20层. 2)跳表的第0层包含所有的元素. 3)每一层都是一个有序的链表.层数越 ...