http基础知识点:小结一下HTTP规范中的那些容易掉进去的坑
http基础知识点:小结一下HTTP规范中的那些容易掉进去的坑这个是个史诗级的大坑,我曾经被这个协议冲突坑了一天。fetch('/page' { Headers: { "Content-Type": "text/plain;charset=UTF-8" } referrer: "https://demo.com/anotherpage" // <- referrerPolicy: "no-referrer-when-downgrade" // <- }); 一句话总结:凡是涉及到 Referrer 的,除了 HTTP 字段是错的,浏览器的相关配置字段拼写都是正确的。虽然 HTTP 标准里把 Referer 写错了,但是其它可以控制 Referer 的标准并没有将错就错。例如禁止网页自动携带 Referer 头的 <meta
HTTP 协议可以说是开发者最熟悉的一个网络协议,「简单易懂」和「易于扩展」两个特点让它成为应用最广泛的应用层协议。
虽然有诸多的优点,但是在协议定义时因为诸多的博弈和限制,还是隐藏了不少暗坑,让人一不小心就会陷入其中。本文总结了 HTTP 规范中常见的几个暗坑,希望大家开发中有意识的规避它们,提升开发体验。
1.Referer
HTTP 标准把 Referrer 写成 Referer(少些了一个 r),可以说是计算机历史上最著名的一个错别字了。
Referer 的主要作用是携带当前请求的来源地址,常用在防爬虫和防盗链上。前段时间闹的沸沸扬扬的新浪图床挂图事件,就是因为新浪图床突然开始检查 HTTP Referer 头,非新浪域名就不返回图片,导致很多蹭流量的中小博客图都挂了。
虽然 HTTP 标准里把 Referer 写错了,但是其它可以控制 Referer 的标准并没有将错就错。
例如禁止网页自动携带 Referer 头的 <meta> 标签,相关关键字拼写就是正确的:
<!-- 全局禁止发送 referrer -->
<meta name="referrer" content="no-referrer" />
还有一个值得注意的是浏览器的网络请求。从安全性和稳定性上考虑,Referer 等请求头在网络请求时,只能由浏览器控制,不能直接操作,我们只能通过一些属性进行控制。比如说 Fetch 函数,我们可以通过 referrer 和 referrerPolicy 控制,而它们的拼写也是正确的:
fetch('/page' {
Headers: {
"Content-Type": "text/plain;charset=UTF-8"
}
referrer: "https://demo.com/anotherpage" // <-
referrerPolicy: "no-referrer-when-downgrade" // <-
});
一句话总结:
凡是涉及到 Referrer 的,除了 HTTP 字段是错的,浏览器的相关配置字段拼写都是正确的。
二.「灵异」的空格
1. 还是 ?
这个是个史诗级的大坑,我曾经被这个协议冲突坑了一天。
开始讲解前先看个小测试,在浏览器里输入 blank test( blank 和 test 间有个空格),我们看看浏览器如何处理的:
从动图可以看出浏览器把空格解析为一个加号「 」。
是不是感觉有些奇怪?我们再做个测试,用浏览器提供的几个函数试一下:
encodeURIComponent("blank test") // "blank test"
encodeURI("q=blank test") // "q=blank test"
new URLSearchParams("q=blank test").toString() // "q=blank test"
代码是不会说谎的,其实上面的结果都是正确的,encode 结果不一样,是因为 URI 规范和 W3C 规范冲突了,才会搞出这种让人疑惑的乌龙事件。
2.冲突的协议
我们首先看看 URI 中的保留字,这些保留字不参与编码。保留字符一共有两大类:
- gen-delims:: / ? # [ ] @
- sub-delims:! $ & ' ( ) * ; =
URI 的编码规则也很简单,先把非限定范围的字符转为 16 进制,然后前面加百分号。
空格这种不安全字符转为十六进制就是 0x20,前面再加上百分号 % 就是 :
所以这时候再看 encodeURIComponent 和 encodeURI 的编码结果,就是完全正确的。
既然空格转为 是正确的,那转为 是怎么回事?这时候我们就要了解一下 HTML form 表单的历史。
早期的网页没有 AJAX 的时候,提交数据都是通过 HTML 的 form 表单。form 表单的提交方法可以用 GET 也可以用 POST,大家可以在 MDN form 词条上测试:
经过测试我们可以看出表单提交的内容中,空格都是转为加号的,这种编码类型就是 application/x-www-form-urlencoded,在 WHATWG 规范里是这样定义的:
到这里基本上就破案了,URLSearchParams 做 encode 的时候,就按这个规范来的。我找到了 URLSearchParams 的 Polyfill 代码,里面就做了 到 的映射:
replace = {
'!': '!'
"'": '''
'(': '('
')': ')'
'~': '~'
' ': ' ' // <= 就是这个
'