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. 【Java8新特性】还没搞懂函数式接口?赶快过来看看吧!

    写在前面 Java8中内置了一些在开发中常用的函数式接口,极大的提高了我们的开发效率.那么,问题来了,你知道都有哪些函数式接口吗? 函数式接口总览 这里,我使用表格的形式来简单说明下Java8中提供的 ...

  2. Dubbo(七):redis注册中心的应用

    上篇我们讲了Dubbo中有一个非常本质和重要的功能,那就是服务的自动注册与发现,而这个功能是通过注册中心来实现的.上篇中使用zookeeper实现了注册中心的功能,同时了提了dubbo中有其他许多的注 ...

  3. maven基本配置

    1.maven 是一个项目构建工具,如果在公司做大的项目 ,需要把项目拆分成很多子项目,为了方便各子项目之间协同开发和调试,一般都会使用maven.使用maven和以前web项目最大的不同是jar包的 ...

  4. Kitty Cloud(HTTP_RPC)的全局异常处理

    项目地址 https://github.com/yinjihuan/kitty-cloud 异常处理不用我讲,大家都清楚.单独的异常处理太繁琐,全局异常处理可以在一个应用中统一进行异常的处理,非常方便 ...

  5. LeetCode 102. 二叉树的层序遍历 | Python

    102. 二叉树的层序遍历 题目来源:https://leetcode-cn.com/problems/binary-tree-level-order-traversal 题目 给你一个二叉树,请你返 ...

  6. 关于fromdata的上传文件问题

    <div <label>上传pdf</label> <input id="fileId" type="file" accep ...

  7. 这或许是最详细的JUC多线程并发总结

    多线程进阶---JUC并发编程 完整代码传送门,见文章末尾 1.Lock锁(重点) 传统 Synchronizd package com.godfrey.demo01; /** * descripti ...

  8. 将mat文件中的数据按要求保存到txt文档中(批处理)

    之前有个老朋友,让帮忙将一个mat中的数据重新保存到txt中,由于数据比较多需要用到批处理,之前弄过很多次,但每次一到要用的时候总是忘记怎么写了,现在记录一下,免得后面老是需要上网搜.这里先说一个比较 ...

  9. java读取文件内容常见几种方式

    ①随机读取文件内容 ②以行为单位读取文件,常用于读面向行的格式化文件 ③以字符为单位读取文件,常用于读文本,数字等类型的文件 ④以字节为单位读取文件,常用于读二进制文件,如图片.声音.影像等文件 pa ...

  10. gRPC负载均衡(自定义负载均衡策略)

    前言 上篇文章介绍了如何实现gRPC负载均衡,但目前官方只提供了pick_first和round_robin两种负载均衡策略,轮询法round_robin不能满足因服务器配置不同而承担不同负载量,这篇 ...