You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 18 | 四通八达HTTP的重定向和跳转
在专栏[第1讲](https://time.geekbang.org/column/article/97837)时我曾经说过,为了实现在互联网上构建超链接文档系统的设想,蒂姆·伯纳斯-李发明了万维网使用HTTP协议传输“超文本”让全世界的人都能够自由地共享信息。
“超文本”里含有“超链接”,可以从一个“超文本”跳跃到另一个“超文本”,对线性结构的传统文档是一个根本性的变革。
能够使用“超链接”在网络上任意地跳转也是万维网的一个关键特性。它把分散在世界各地的文档连接在一起,形成了复杂的网状结构,用户可以在查看时随意点击链接、转换页面。再加上浏览器又提供了“前进”“后退”“书签”等辅助功能,让用户在文档间跳转时更加方便,有了更多的主动性和交互性。
那么点击页面“链接”时的跳转是怎样的呢具体一点比如在Nginx的主页上点了一下“download”链接会发生什么呢
结合之前的课程稍微思考一下你就能得到答案浏览器首先要解析链接文字里的URI。
```
http://nginx.org/en/download.html
```
再用这个URI发起一个新的HTTP请求获取响应报文后就会切换显示内容渲染出新URI指向的页面。
这样的跳转动作是由浏览器的使用者主动发起的,可以称为“**主动跳转**”,但还有一类跳转是由服务器来发起的,浏览器使用者无法控制,相对地就可以称为“**被动跳转**”这在HTTP协议里有个专门的名词叫做“**重定向**”Redirection
## 重定向的过程
其实之前我们就已经见过重定向了,在[第12讲](https://time.geekbang.org/column/article/102483)里3××状态码时就说过301是“永久重定向”302是“临时重定向”浏览器收到这两个状态码就会跳转到新的URI。
那么,它们是怎么做到的呢?难道仅仅用这两个代码就能够实现跳转页面吗?
先在实验环境里看一下重定向的过程吧用Chrome访问URI “/18-1”它会使用302立即跳转到“/index.html”。
![](https://static001.geekbang.org/resource/image/ad/2d/ad5eb7546ee7ef62a9987120934b592d.png)
从这个实验可以看到这一次“重定向”实际上发送了两次HTTP请求第一个请求返回了302然后第二个请求就被重定向到了“/index.html”。但如果不用开发者工具的话你是完全看不到这个跳转过程的也就是说重定向是“用户无感知”的。
我们再来看看第一个请求返回的响应报文:
![](https://static001.geekbang.org/resource/image/a4/ac/a4276db758bf90f63fd5a5c2357af4ac.png)
这里出现了一个新的头字段“Location: /index.html”它就是301/302重定向跳转的秘密所在。
“**Location**”字段属于响应字段必须出现在响应报文里。但只有配合301/302状态码才有意义它**标记了服务器要求重定向的URI**这里就是要求浏览器跳转到“index.html”。
浏览器收到301/302报文会检查响应头里有没有“Location”。如果有就从字段值里提取出URI发出新的HTTP请求相当于自动替我们点击了这个链接。
在“Location”里的URI既可以使用绝对URI也可以使用相对URI。所谓“绝对URI”就是完整形式的URI包括scheme、host:port、path等。所谓“相对URI”就是省略了scheme和host:port只有path和query部分是不完整的但可以从请求上下文里计算得到。
例如刚才的实验例子里的“Location: /index.html”用的就是相对URI。它没有说明访问URI的协议和主机但因为是由“[http://www.chrono.com/18-1](http://www.chrono.com/18-1)”重定向返回的响应报文所以浏览器就可以拼出完整的URI
```
http://www.chrono.com/index.html
```
实验环境的URI“/18-1”还支持使用query参数“dst=xxx”指明重定向的URI你可以用这种形式再多试几次重定向看看浏览器是如何工作的。
```
http://www.chrono.com/18-1?dst=/15-1?name=a.json
http://www.chrono.com/18-1?dst=/17-1
```
注意在重定向时如果只是在站内跳转你可以放心地使用相对URI。但如果要跳转到站外就必须用绝对URI。
例如如果想跳转到Nginx官网就必须在“nginx.org”前把“http://”都写出来否则浏览器会按照相对URI去理解得到的就会是一个不存在的URI“[http://www.chrono.com/nginx.org”](http://www.chrono.com/nginx.org%E2%80%9D)
```
http://www.chrono.com/18-1?dst=nginx.org #错误
http://www.chrono.com/18-1?dst=http://nginx.org #正确
```
![](https://static001.geekbang.org/resource/image/00/aa/006059602ee75b176a80429f49ffc9aa.png)
那么如果301/302跳转时没有Location字段会怎么样呢
这个你也可以自己试一下使用第12讲里的URI“/12-1”查询参数用“code=302”
```
http://www.chrono.com/12-1?code=302
```
## 重定向状态码
刚才我把重定向的过程基本讲完了,现在来说一下重定向用到的状态码。
最常见的重定向状态码就是301和302另外还有几个不太常见的例如303、307、308等。它们最终的效果都差不多让浏览器跳转到新的URI但语义上有一些细微的差别使用的时候要特别注意。
**301**俗称“永久重定向”Moved Permanently意思是原URI已经“永久”性地不存在了今后的所有请求都必须改用新的URI。
浏览器看到301就知道原来的URI“过时”了就会做适当的优化。比如历史记录、更新书签下次可能就会直接用新的URI访问省去了再次跳转的成本。搜索引擎的爬虫看到301也会更新索引库不再使用老的URI。
**302**俗称“临时重定向”“Moved Temporarily”意思是原URI处于“临时维护”状态新的URI是起“顶包”作用的“临时工”。
浏览器或者爬虫看到302会认为原来的URI仍然有效但暂时不可用所以只会执行简单的跳转页面不记录新的URI也不会有其他的多余动作下次访问还是用原URI。
301/302是最常用的重定向状态码在3××里剩下的几个还有
* 303 See Other类似302但要求重定向后的请求改为GET方法访问一个结果页面避免POST/PUT重复操作
* 307 Temporary Redirect类似302但重定向后请求里的方法和实体不允许变动含义比302更明确
* 308 Permanent Redirect类似307不允许重定向后的请求变动但它是301“永久重定向”的含义。
不过这三个状态码的接受程度较低,有的浏览器和服务器可能不支持,开发时应当慎重,测试确认浏览器的实际效果后才能使用。
## 重定向的应用场景
理解了重定向的工作原理和状态码的含义,我们就可以**在服务器端拥有主动权**,控制浏览器的行为,不过要怎么利用重定向才好呢?
使用重定向跳转,核心是要理解“**重定向**”和“**永久/临时**”这两个关键词。
先来看什么时候需要重定向。
一个最常见的原因就是“**资源不可用**”需要用另一个新的URI来代替。
至于不可用的原因那就很多了。例如域名变更、服务器变更、网站改版、系统维护这些都会导致原URI指向的资源无法访问为了避免出现404就需要用重定向跳转到新的URI继续为网民提供服务。
另一个原因就是“**避免重复**”让多个网址都跳转到一个URI增加访问入口的同时还不会增加额外的工作量。
例如有的网站都会申请多个名称类似的域名然后把它们再重定向到主站上。比如你可以访问一下“qq.com”“github.com ”“bing.com”记得事先清理缓存看看它是如何重定向的。
决定要实行重定向后接下来要考虑的就是“永久”和“临时”的问题了也就是选择301还是302。
301的含义是“**永久**”的。
如果域名、服务器、网站架构发生了大幅度的改变比如启用了新域名、服务器切换到了新机房、网站目录层次重构这些都算是“永久性”的改变。原来的URI已经不能用了必须用301“永久重定向”通知浏览器和搜索引擎更新到新地址这也是搜索引擎优化SEO要考虑的因素之一。
302的含义是“**临时**”的。
原来的URI在将来的某个时间点还会恢复正常常见的应用场景就是系统维护把网站重定向到一个通知页面告诉用户过一会儿再来访问。另一种用法就是“服务降级”比如在双十一促销的时候把订单查询、领积分等不重要的功能入口暂时关闭保证核心服务能够正常运行。
## 重定向的相关问题
重定向的用途很多,掌握了重定向,就能够在架设网站时获得更多的灵活性,不过在使用时还需要注意两个问题。
第一个问题是“**性能损耗**”。很明显,重定向的机制决定了一个跳转会有两次请求-应答,比正常的访问多了一次。
虽然301/302报文很小但大量的跳转对服务器的影响也是不可忽视的。站内重定向还好说可以长连接复用站外重定向就要开两个连接如果网络连接质量差那成本可就高多了会严重影响用户的体验。
所以重定向应当适度使用,决不能滥用。
第二个问题是“**循环跳转**”。如果重定向的策略设置欠考虑可能会出现“A=>B=>C=>A”的无限循环不停地在这个链路里转圈圈后果可想而知。
所以HTTP协议特别规定浏览器必须具有检测“循环跳转”的能力在发现这种情况时应当停止发送请求并给出错误提示。
实验环境的URI“/18-2”就模拟了这样的一个“循环跳转”它跳转到“/18-1”并用参数“dst=/18-2”再跳回自己实现了两个URI的无限循环。
使用Chrome访问这个地址会得到“该网页无法正常运作”的结果
![](https://static001.geekbang.org/resource/image/4b/2f/4b91aeea08d90f173c62493934e5f52f.png)
## 小结
今天我们学习了HTTP里的重定向和跳转简单小结一下这次的内容
1. 重定向是服务器发起的跳转要求客户端改用新的URI重新发送请求通常会自动进行用户是无感知的
2. 301/302是最常用的重定向状态码分别是“永久重定向”和“临时重定向”
3. 响应头字段Location指示了要跳转的URI可以用绝对或相对的形式
4. 重定向可以把一个URI指向另一个URI也可以把多个URI指向同一个URI用途很多
5. 使用重定向时需要当心性能损耗,还要避免出现循环跳转。
## 课下作业
1. 301和302非常相似试着结合第12讲用自己的理解再描述一下两者的异同点。
2. 你能结合自己的实际情况,再列出几个应当使用重定向的场景吗?
欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
![unpreview](https://static001.geekbang.org/resource/image/9b/05/9b873d25e33f86bb2818fc8b50fbff05.png)