当Ajax遇上302, 孽缘!
什么是 HTTP 状态码 302 ?
根据MDN的文档
HTTP302Found
重定向状态码表明请求的资源被暂时的移动到了由Location
头部指定的 URL 上。浏览器会重定向到这个URL, 但是搜索引擎不会对该资源的链接进行更新
以及
有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问。在这种情况下可以使用临时重定向。
搜索引擎不会记录该新的、临时的链接。在创建、更新或者删除资源的时候,临时重定向也可以用于显示临时性的进度页面。
302的使用场景
在 express.js 中,res.redirect(url) 实际上就是设置了302 HTTP status
app.get('/doc', (req, res) => {
const redirectUrl = '/temp-doc';
res.redirect(redirectUrl);
})
可以暂时将 /doc 重定向到另一个api /temp-doc
另外,在 OAuth 流程中,302 也被多次用到 (参考我关于Oauth的文章 FreewheelLee:非常详细!深入理解OAuth原理和实践细节 )
当 AJAX 遇上 302
大多数 302 的使用场景都是比较简单直接且同步的 —— 即用户在浏览器里输入一个URL,这个URL的背后服务器返回一个302状态和Location header,浏览器就自动重定向到Location指定的新地址上。
但是随着Ajax的广泛使用,尤其是SPA(单页面应用)的流行,越来越多的网络请求都是通过Ajax发起的,而Ajax的目标URL如果返回 302 就开始带来一些问题。
AJAX + 302 + CORS !?
首当其冲的就是跨域问题。
以OAuth流程为例
如果使用 Ajax 对以下这个 /login API 发起请求,就会发生CORS 错误。
而在浏览器中直接输入 /login 完整地址则是没问题的。
为什么呢?—— 因为跨域了。
细节:
- 用户打开目标网站,假设为
2. 某个动作触发了 AJAX 发起请求
2. 后端返回 302 ,Location 值为
3. 浏览器 自动 把 Ajax 的目标url 切成新的地址,即 发生 Ajax 请求到
4. 由于这是个Ajax请求,且用户处在 you-app-domain 的域名上,而请求地址为 github 域名,因此这是个跨域请求
5. 显然 github 不可能随便对其他域名打开 CORS 限制,因此这个 ajax 就失败了。(对 CORS 不熟悉的读者请自行搜索学学习)
痛点
AJAX + 302 + CORS 问题的痛点在于 —— 浏览器 自动 发起对 重定向地址的请求,js 无法插手干预。
解决方案
浏览器的“自动行为”其实是因为 302 状态码的设计规范,换句话说,如果不使用 302 就能解决这种“自动行为”,因此,
解决方案一 —— 返回200而不是302
后端API可以返回200状态码的同时在内容中加入重定向地址的信息,如
app.get('/login', (req, res) => {
const redirectUrl = `https://github.com/login/oauth/authorize?client_id=xxxx`;
res.status(200);
res.send({
redirectUrl: redirectUrl