302重定向

4951

前言

一个需要登陆的网站通常会有登录校验机制,在一定时间内用户无操作,登录便过期了。这时候再刷新页面或点击任意发送请求的按钮,页面就会自动重定向到登录页,等用户登录后再跳转到原来的页面。

事项

这种情况下涉及到的重定向一般为302(临时重定向)

http服务发现响应状态码是302时,便会根据响应头的Location自动发送另一个请求。

JS不会接收到上一个请求的任何消息,也就是说,JS无法获取302请求的信息。如图。

302-get演示
302-get演示

点击第二个按钮发送了一个/foo的get请求,该get请求重定向到bar。随即页面便发送了/bar请求,并且xhr有关输出内容也都是关于/bar的。

302-post 演示
302-post 演示

点击第三个按钮,与第二个按钮的结果一样,也是重新发送了一个重定向请求。(其余两个请求是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的自动跳转
/foo的自动跳转

请求与客户端的页面是一致的,唯一的区别就是页面自动从/foo跳转到了/bar

由此猜测,当页面地址与服务器地址一致时,便会自动跳转到重定向页面,否则只是发起一个请求。

总结

  1. http状态码是302时,浏览器会根据响应头的Location自动发送另一个请求,包括post请求
  2. JS接收不到302的任何信息
  3. 页面地址与服务器地址一致时,才会发送自动跳转(大概是的)

附代码

客户端 请使用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({ extendedfalse }));

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}`);
});