HTTP 协议可以说是开发者最熟悉的一个网络协议,「简单易懂」和「易于扩展」两个特点让它成为应用最广泛的应用层协议。

虽然有诸多的优点,但是在协议定义时因为诸多的博弈和限制,还是隐藏了不少暗坑,让人一不小心就会陷入其中。本文总结了 HTTP 规范中常见的几个暗坑,希望大家开发中有意识的规避它们,提升开发体验。

1.Referer

HTTP 标准把 Referrer 写成 Referer(少些了一个 r),可以说是计算机历史上最著名的一个错别字了。

Referer 的主要作用是携带当前请求的来源地址,常用在反爬虫和防盗链上。前段时间闹的沸沸扬扬的新浪图床挂图事件,就是因为新浪图床突然开始检查 HTTP Referer 头,非新浪域名就不返回图片,导致很多蹭流量的中小博客图都挂了。

虽然 HTTP 标准里把 Referer 写错了,但是其它可以控制 Referer 的标准并没有将错就错。

例如禁止网页自动携带 Referer 头的 <meta> 标签,相关关键字拼写就是正确的:

  1. <!-- 全局禁止发送 referrer -->
    <meta name="referrer" content="no-referrer" />

还有一个值得注意的是浏览器的网络请求。从安全性和稳定性上考虑,Referer 等请求头在网络请求时,只能由浏览器控制,不能直接操作,我们只能通过一些属性进行控制。比如说 Fetch 函数,我们可以通过 referrerreferrerPolicy 控制,而它们的拼写也是正确的:

  1. fetch('/page', {
      headers: {
        "Content-Type": "text/plain;charset=UTF-8"
      },
      referrer: "https://demo.com/anotherpage", // <-
      referrerPolicy: "no-referrer-when-downgrade", // <-
    });

一句话总结:

凡是涉及到 Referrer 的,除了 HTTP 字段是错的,浏览器的相关配置字段拼写都是正确的。

二.「灵异」的空格

1.%20 还是 +

这个是个史诗级的大坑,我曾经被这个协议冲突坑了一天。

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

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

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

  1. 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

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

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

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

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

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

  1. 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 表单的普及,这种格式已经推广开了

其实上面一大段句话就是一个意思:这玩意儿设计的就是

HTTP 规范中的那些暗坑的更多相关文章

  1. JavaScript中this的一些坑

    我们经常在回调函数里面会遇到一些坑: var obj = { name: 'qiutc', foo: function() { console.log(this); }, foo2: function ...

  2. 项目中踩过的坑之-sessionStorage

    总想写点什么,却不知道从何写起,那就从项目中踩过的坑开始吧,希望能给可能碰到相同问题的小伙伴一点帮助. 项目情景: 有一个id,要求通过当前网页打开一个新页面(不是当前页面),并把id传给打开的新页面 ...

  3. OWIN规范中最让人费解的地方

    OWIN defines a standard interface between .NET web servers and web applications. OWIN最让人费解不是OWIN的五大角 ...

  4. MANIFEST.INF!JAR规范中

    MANIFEST.INF!JAR规范中 META-INF 目录中内容心得.顺带整理了网上资料,提供地址 标签: jarjava产品sunantapache 2012-03-31 17:09 2768人 ...

  5. 使用ffmpeg视频编码过程中踩的一个坑

           今天说说使用ffmpeg在写视频编码程序中踩的一个坑,这个坑让我花了好多时间,回头想想,非常多时候一旦思维定势真的挺难突破的.以下是不对的编码结果:                   ...

  6. 细数Python Flask微信公众号开发中遇到的那些坑

    最近两三个月的时间,断断续续边学边做完成了一个微信公众号页面的开发工作.这是一个快递系统,主要功能有用户管理.寄收件地址管理.用户下单,订单管理,订单查询及一些宣传页面等.本文主要细数下开发过程中遇到 ...

  7. 小程序中曾经遇到的坑(1)----canvas画布

    目前正在开发小程序,在开发过程中,总会遇到一些坑,而这些坑并不会有很多开发者遇到而说出来.这里先记录一条我开发过程中遇到的问题,以便后人在开发中能够更容易的解决问题!!! 首先,小程序在canvas画 ...

  8. 记一次SpringBoot 开发中所遇到的坑和解决方法

    记一次SpringBoot 开发中所遇到的坑和解决方法 mybatis返回Integer为0,自动转型出现空指针异常 当我们使用Integer去接受数据库中表的数据,如果返回的数据中为0,那么Inte ...

  9. OpenGL ES SL 3.0规范中以前的attribute改成了in varying改成了out

           OpenGL ES和OpenGL的图标 关于“OpenGL ES SL 3.0规范中以前的attribute改成了in varying改成了out”这个问题,做一阐述: 1.关键字的小修 ...

随机推荐

  1. pomelo安装笔记

    npm install -dnpm config set registry https://registry.npm.taobao.orgnpm install pomelo -gpomelo lis ...

  2. DAG的深度优先搜索标记

    这是图论的基础知识点,也是学习Tarjan的导学课. 一.知识 对于在图G上进行深度优先搜索算法所产生的深度优先森林Gt,我们可以定义四种边的类型: 1.树边(Tree Edge):为深度优先森林中G ...

  3. P5057 【[CQOI2006]简单题】

    洛谷P5057[CQOI2006]简单题 差分 树状数组基本操作不说了,主要想记录一下异或下的差分 a数组为每一位的真实值(假设\(a[0]=0\)),t为差分后的数组 则\(t[i]=a[i]\)^ ...

  4. css实现水平垂直居中的几种方式

    梳理下平时常用css水平垂直居中方式- 使用flex布局 HTML <div class="box"> <div class="child"& ...

  5. LeetCode 45. 跳跃游戏 II | Python

    45. 跳跃游戏 II 题目来源:https://leetcode-cn.com/problems/jump-game-ii 题目 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素 ...

  6. Blazor一个简单的示例让我们来起飞

    Blazor Blazor他是一个开源的Web框架,不,这不是重点,重点是它可以使c#开发在浏览器上运行Web应用程序.它其实也简化了SPA的开发过程. Blazor = Browser + Razo ...

  7. 用 GitHub Action 构建一套 CI/CD 系统

    ​ 缘起 Nebula Graph 最早的自动化测试是使用搭建在 Azure 上的 Jenkins,配合着 GitHub 的 Webhook 实现的,在用户提交 Pull Request 时,加个 r ...

  8. Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

    今天上传图片遇到这个报错 百度了下,网上说是input标签type=file所以导致的问题,可是我的type=hidden 解决办法: 把上面的代码改成如下问题就解决了

  9. SpringBoot:整合Shiro

    目录 1.Shiro简介 1.1.什么是Shiro? 1.2.有哪些功能 1.3.Shiro架构(外部) 1.4.Shiro架构(内部) 2.HelloWorld 3.Shiro整合Spring Bo ...

  10. Java基础-数据类型的拓展

    整数拓展:进制 二进制 0b开头 十进制 八进制 0开头 十六进制 0x开头 0~9 A~F 16 int i = 10; int i1 = 010;//八进制 0开头 int i2 = 0x10;/ ...