四通八达:HTTP的重定向和跳转
“超文本”里含有“超链接”,可以从一个“超文本”跳跃到另一个“超文本”,对线性结构的传统文档是一个根本性的变革。能够使用“超链接”在网络上任意地跳转也是万维网的一个关键特性。它把分散在世界各地的文档连接在一起,形成了复杂的网状结构,用户可以在查看时随意点击链接、转换页面。由浏览器的使用者主动发起的,可以称为“主动跳转”,但还有一类跳转是由服务器来发起的,浏览器使用者无法控制,相对地就可以称为“被动跳转”,这在 HTTP 协议里有个专门的名词,叫做“重定向”(Redirection)。
一、重定向的过程
先在实验环境里看一下重定向的过程吧,用 Chrome 访问 URI “/18-1”,它会使用 302 立即跳转到“/index.html”。
这里出现了一个新的头字段“Location: /index.html”,它就是 301/302 重定向跳转的秘密所在。
“Location”字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI,这里就是要求浏览器跳转到“index.html”。浏览器收到 301/302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。
例如,刚才的实验例子里的“Location: /index.html”用的就是相对 URI。它没有说明访问 URI 的协议和主机,但因为是由“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/18-1?dst=nginx.org # 错误 http://www.chrono.com/18-1?dst=http://nginx.org # 正确
二、重定向状态码
最常见的重定向状态码就是 301 和 302,另外还有几个不太常见的,例如 303、307、308 等。它们最终的效果都差不多,让浏览器跳转到新的 URI,但语义上有一些细微的差别,使用的时候要特别注意。
301俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。
302俗称“临时重定向”(“Moved Temporarily”),意思是原 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,增加访问入口的同时还不会增加额外的工作量。
四、重定向相关问题
第一个问题是“性能损耗”。很明显,重定向的机制决定了一个跳转会有两次请求 - 应答,比正常的访问多了一次。
第二个问题是“循环跳转”。如果重定向的策略设置欠考虑,可能会出现“A=>B=>C=>A”的无限循环,不停地在这个链路里转圈圈,后果可想而知。
所以 HTTP 协议特别规定,浏览器必须具有检测“循环跳转”的能力,在发现这种情况时应当停止发送请求并给出错误提示。
至此,结束。