彻底搞懂 HTTP 3XX 重定向状态码和浏览器重定向
时间:2023-02-09 07:36:01 | 来源:建站知识
时间:2023-02-09 07:36:01 来源:建站知识
最近好好研究了一下 HTTP 的 3XX 状态码,发现这里面涉及到的知识点还挺多的,很多在我们日常开发过程中也会用到,于是就想通过这篇文章来和大家聊一聊它们的含义以及应用场景。
3XX 状态码是关于重定向的,常见的状态码有:301,302,303,304,307 和 308。这些状态码大致可以分为三类,其中包括:
- 永久重定向:301,308
- 临时重定向:302,303, 307
- 其他重定向:304
这篇文章主要分析永久重定向和临时重定向相关的状态码,至于其他重定向就不在这里细讲了。
PS: 正文里提到的 3XX 响应只包含永久重定向和临时重定向所对应的 HTTP 状态码,不包括其他重定向。
开始
我们既需要明白服务器发送的各种重定向状态码是什么含义,还需要搞清楚浏览器收到这些重定向状态码之后会怎么做。因此本文会从服务器和浏览器两个部分进行讲解。
服务器
永久重定向
永久重定向,是指用户请求的资源地址已经废弃了,现在需要使用新地址来访问,并通过响应 Header 的 Location 字段将这个新的地址告知给用户。
就好比胡同口有一家我们常去的饭店,但是由于旧城改造这家店搬迁到了另一个地方。有一天我们准备去这家店吃饭,发现门口张贴告示:此店已迁移到 XXX,并附上了详细的新地址。也就是说,之后如果我还想去这家店吃饭,只能去新的地址。
301 Moved Permanently
表示请求资源已经被移动到了由 Location 头部指定的 URI 上,是固定的不会再改变。在用新 URL 发起请求时,对于 GET 请求保持不变,但是对于 POST 请求,尽管标准要求浏览器在收到该响应时不应该修改 HTTP Method 和 Request Body,但是大多数浏览器没有遵守这个标准,会把原本为 POST 的请求重定向到 GET 请求上。
我们拿 Chrome 浏览器测试了一下,结果如下:
从上面的截图中可以看出,浏览器在收到 302 响应之后,从 Location 里面获取到了 /pets 这个新的地址,并发起了新的请求。但是,这个新的请求使用的是 GET 方法而非我们原来的 POST 方法,Request Body 也丢失了。
应用场景:
比如某个应用进行了整站重构,重构之后 URL 发生了改变,这时候可以考虑将老 URL 重定向到新 URL。
308 Permanent Redirect
跟 301 一样,唯一的区别就是浏览器不会改变请求的 HTTP Method 和 Request Body。
我们又继续拿 Chrome 测试了一下,结果如下:
可以看出,使用 308 之后,请求的 Method 和 Request Body 都没有发生改变,还是跟原来一样。
临时重定向
由于某些原因,导致用户请求的资源地址临时不可用,但其他某个地址是可访问的,于是就通过响应 Header 的 Location 字段将这个临时地址告知给用户。
就好比我们去一家饭店吃饭,到门口发现老板张贴告示:家里临时有事,请去附近另一个分店用餐,并附上了分店的详细地址。这样,我们就可以先临时去这家分店用餐,等之后老板回来了,我们又可以在原来这家店继续用餐。
302 Found
在收到 302 响应之后,浏览器会发起新的请求,尽管标准要求浏览器在收到该响应时不应该修改 HTTP Method 和 Request Body,但是大多数浏览器都没有遵守这个标准。大多数浏览器会将 302 请求视为 303 请求,也就是说 302 和 303 几乎是一样的。
应用场景举例:通常图片资源会放到类似 S3 这样的静态资源服务器,出于对隐私和安全的考虑,我们有时不能直接通过静态资源服务器的 URL 访问到这个图片,而是需要后端通过身份凭证去 S3 签发一个临时地址(这个地址用一次就失效了,下一次需要重新签发),然后我们才能通过这个临时地址访问到真正的图片资源。
对于这个场景,302/303 就非常有用了。比如,前端可以使用一个固定的 URL 来访问图片资源 (<img src="/image/foo.jpg"),这个 URL 由后端提供,后端在生成完临时地址之后,可以直接通过 302/303 重定向到这个新的地址,接下来浏览器会再次发起请求,获取新地址指向的这个图片资源。
303 See Other
浏览器在收到 303 响应之后,除 GET 方法保持不变之外,其他所有方法都会被改为 GET 方法,同时 Request Body 也会丢失。一般用于将 POST 方法重定向到 GET 方法。
应用场景举例:假设我们有一个非常原始的表单,这个表单是通过在 HTML 上设置 action 和 method 来提交的。在表单提交之后,浏览器会自动跳转到 action 所在的地址,并使用 method 所指定的方法向服务器发起请求,在收到服务器的303 响应之后,又需要跳转回原来这个页面。
<form action="http://example.com/api/form" method="POST"> <input type="text" name="name" placeholder="name"> <input type="email" name="email" placeholder="email"> <input type="submit" value="Submit"></form>
这里就会出现一个问题,假设我们页面的 URL 是 http://example.com ,我们只能通过 GET 方法请求这个页面。而提交表单所使用的 URL 是 http://example.com/api/form ,我们只能通过 POST 方法来调用这个接口。在使用 POST 方法提交表单之后,如果我们想返回原来的页面,只能使用 GET 方法发起请求,因此我们需要使用 303 状态码,将原来的 POST 方法改成 GET。
307 Temporary Redirect
307 和 303 一样,唯一的区别就是浏览器不会改变请求的 HTTP Method 和 Request Body。对于 POST/PUT 等非 GET 请求很有用。
应用场景:在上传文件时,我们通常会调用后端提供的某个 API (/upload),但由于上传会占用大量的服务器资源和带宽,这个 API 一般不会处理真正的上传,而是通过另一个专门的上传服务来完成。这意味着我们需要调用两次 API 才能完成上传功能,一次是调用后端接口获取真正的上传地址,另一次是携带 Request Body 请求这个上传地址,完成真正的上传。
如果让两个 API 的请求参数和方法保持一致,那么对于前端来说,只需要发起一次请求即可,后端在生成完上传地址后,通过 307 重定向到这个地址,然后浏览器会使用之前的 Request Body 和 HTTP Method 请求这个新的地址,这样就完成了上传。
注意,如果使用 302 会导致上传失败,原因是 302 会将 PUT 改为 GET 请求,同时 Request Body 也会丢失。
其他常见的重定向场景
域名别称
- 扩大站点的用户覆盖面。比如访问 http://www.google.com 会被重定向到 http://google.com
- 迁移到另一个域名。
- 强制使用 HTTPS 协议。比如访问 http://baidu.com 会被重定向到 https://baidu.com
保持链接有效
当重构网站时,可能会让资源的 URL 发生改变。但我们并不想因此使旧链接失效,因为这些链接不仅可以给你带来宝贵的用户,还能够帮助优化 SEO,因此需要建立从旧链接到新链接的重定向映射。
浏览器
我们已经了解了各种关于重定向的 HTTP 状态码,那么浏览器收到这些状态码之后会做些什么呢?当浏览器收到 3XX 响应之后,是否会将地址栏的 URL 修改为响应 Header 中 Location 字段所指定的 URL,并跳转到这个新的 URL 呢? 请接着往下看。
收到 3XX 响应后,浏览器是否会改变地址栏并跳转?
浏览器是否会改变地址栏并跳转, 其实取决于
请求的 URL 是否会让文档加载/重新加载。这里所指的文档加载/重新加载,既包括当我们通过浏览器地址栏访问某个 URL、刷新页面所导致的文档加载,也包括我们通过
window.location
让文档用新的 URL 加载。只有在这些情况下,如果我们请求的 URL 返回了 3XX 响应,浏览器才会读取响应 Header 里面的 Location 字段,并将浏览器的地址栏修改为这个字段所指定的 URL,然后跳转到这个 URL。
一般来说文档加载/重新加载都会产生一个 Document 类型的请求,如下所示:
PS: React Router 这样的路由跳转,不会让文档重新加载 ( 因为使用的是 History API )
但是,如果是通过 fetch/XMLHttpRequest 调用所获取到的 3XX 响应,浏览器是不会改变当前地址栏的 URL 并跳转的。试想,我们在访问图片时,经常会得到很多 302 响应,如果浏览器要跳转 URL,岂不是会导致整个浏览器崩溃。
window.location + 3XX 响应
window.location 能够让文档用新的 URL 加载,默认会将新的 URL push 到浏览器 history 的路由堆栈里面,如果你想使用 replace 的方式,可以调用 window.location.replace() 方法:
window.location = "http://www.mozilla.org";function reloadPageWithHash() { var initialPage = window.location.pathname; window.location.replace('http://example.com/#' + initialPage);}
应用场景:
在使用 OAuth2 鉴权时(比如微信登录),可以通过 window.location 重定向到自己服务器的授权地址(支持多个平台登录时,可以由后端统一处理),然后服务器会生成一个三方授权点的地址,并通过 302 响应告知给浏览器,浏览器在收到响应之后会跳转到这个三方授权点的 URL(微信登录页),完成授权之后,三方授权页面会通过 window.location 再重定向回我们自己的页面。
通过 window.location 再配合 302 响应,我们可以快速将用户导向三方授权点,不需要加载任何 JS,非常快速方便。
参考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Redirections