前言
一个需要登陆的网站通常会有登录校验机制,在一定时间内用户无操作,登录便过期了。这时候再刷新页面或点击任意发送请求的按钮,页面就会自动重定向到登录页,等用户登录后再跳转到原来的页面。
事项
这种情况下涉及到的重定向一般为302(临时重定向)
http服务发现响应状态码是302时,便会根据响应头的Location
自动发送另一个请求。
JS不会接收到上一个请求的任何消息,也就是说,JS无法获取302请求的信息。如图。
点击第二个按钮发送了一个/foo
的get请求,该get请求重定向到bar。随即页面便发送了/bar
请求,并且xhr有关输出内容也都是关于/bar
的。
点击第三个按钮,与第二个按钮的结果一样,也是重新发送了一个重定向请求。(其余两个请求是OPTIONS,出现原因是此为跨域请求 && Content-Type设置了非application/x-www-form-urlencoded,multipart/form-data,text/plain,罪魁祸首xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8");
,把这句去掉就不会发送OPTIONS了)
页面自动跳转
由上图可知,当我们打开本地客户端服务(前端页面 http://localhost:5000)时,重定向的请求会自动发送其Location指向的请求地址,并没有实现自动页面跳转。
当我们打开服务端服务(http://localhost:3000)时,效果如下。
请求与客户端的页面是一致的,唯一的区别就是页面自动从/foo
跳转到了/bar
由此猜测,当页面地址与服务器地址一致时,便会自动跳转到重定向页面,否则只是发起一个请求。
总结
- http状态码是302时,浏览器会根据响应头的
Location
自动发送另一个请求,包括post请求 - JS接收不到302的任何信息
- 页面地址与服务器地址一致时,才会发送自动跳转(大概是的)
附代码
客户端 请使用serve服务启动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn1">发送bar GET请求</button>
<br />
<button id="btn2">发送foo GET请求 重定向到bar</button>
<br />
<button id="btn3">发送foo POST请求 重定向到bar</button>
<script>
const xhr = new XMLHttpRequest();
function sendBar(params) {
xhr.open("get", "http://localhost:3000/bar");
xhr.send();
}
function sendBarPost(params) {
xhr.open("post", "http://localhost:3000/foo");
// 这个请求头会先发送OPTIONS请求
xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8");
xhr.send(JSON.stringify({ name: "leeeeeee" }));
}
function sendFoo(params) {
xhr.open("get", "http://localhost:3000/foo");
xhr.send();
}
xhr.onreadystatechange = (e) => {
const readystate = e.target.readyState;
console.log("readystate", e.target.responseURL, readystate);
if (readystate === 4) {
console.log("response", xhr.response);
}
};
const btn1 = document.querySelector("#btn1");
btn1.addEventListener("click", () => {
sendBar();
});
const btn2 = document.querySelector("#btn2");
btn2.addEventListener("click", () => {
sendFoo();
});
const btn3 = document.querySelector("#btn3");
btn3.addEventListener("click", () => {
sendBarPost();
});
</script>
</body>
</html>
服务端 核心代码
// index.js
const express = require("express");
const app = express();
const port = 3000;
const router = express.Router();
const bodyParser = require("body-parser"); /*post方法*/
app.use(bodyParser.json()); // 添加json解析
app.use(bodyParser.urlencoded({ extended: false }));
app.all("*", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Content-Type", "application/json;charset=utf-8");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.get("/bar", (req, res) => {
const body = {
data: "Hello Bar!"
};
res.end(JSON.stringify(body));
});
app.get("/foo", (req, res) => {
res.redirect("/bar"); // 302
});
app.post("/foo", function (req, res, next) {
const name = req.body.name; //post专用
const body = {
data: "Hello POST Foo!",
name
};
console.log('body', body);
// res.send(body); //数据返回前端
res.redirect("/bar"); // 302
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});