You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express.js未登录重定向失效:返回200 OK而非预期状态

问题

我使用Express.js开发服务端Web应用,期望用户未设置session时,请求页面能自动重定向到登录页。以下是相关代码:

服务端登录路由代码

app.post("/api/auth/signin", async (req, res) => {
  const username = req.body.username;
  const password = req.body.password;

  console.log(`Request JSON: ${JSON.stringify(req.body)}`);

  const userRequested = await db
    .collection("users")
    .findOne({ username: username });

  if (userRequested == null) {
    res.status(401).json({
      message: "Requested user does not exist",
    });
    res.json(username);
  } else if (username == null || password == null) {
    res.status(401).json({
      message: "Null username or password are not allowed",
    });
  } else {
    if (userRequested.password === password) {
      req.session.user = userRequested;
      res.status(200);
      res.json(userRequested);
      //redirect("/api/budget/whoami");
    } else {
      res.status(401).send({
        message: "Wrong credentials",
      });
    }
  }
});

服务端Session验证中间件

app.use(function(req, res, next) {
  if (!req.session || !req.session.user){
  res.statusCode = 300;
  res.redirect('http://localhost:3000/index.html');
  }   else {
   next();
  }
});

客户端Fetch请求代码

async beforeMount() {
    console.log("Executing on page loading");

    await fetch("/api/budget/whoami", {
      method: "GET",
      redirect: "follow"
    })
      .then((res) => {

        if(res.status == 300){
          window.location.replace("index.html");
        }

          console.log("All good, getting the response");
          return res.json();
      })
      .then((user) => {
        console.log("Updating value");
        console.log(JSON.stringify(user));
        console.log(user.username);

        this.username = user.username;
        this.password = user.password;
        this.firstName = user.firstName;
        this.lastName = user.lastName;
      });
  }

但实际测试发现,即使未登录,用Postman请求时返回的状态码是200 OK,响应体为登录页HTML代码,不符合预期重定向逻辑。请问问题出在哪里?


问题分析与解决

1. 中间件顺序与作用范围错误

Express中间件按注册顺序执行,当前的session验证中间件会拦截所有请求,包括登录页本身和登录接口。这会导致未登录用户请求受保护路由时,服务端试图重定向到登录页,但登录页的静态资源请求又被中间件拦截,最终返回登录页HTML而非重定向响应。

另外,登录接口/api/auth/signin不应该被session验证拦截,否则用户根本无法完成登录操作。需要给中间件添加路径限制,仅对需要保护的API生效。

2. 重定向状态码不规范

你使用了300状态码,但该码表示多重选择,不属于标准重定向状态码。浏览器和Postman不会将其识别为重定向,导致后续逻辑失效。标准重定向状态码应使用302 Found(临时重定向,Express默认)或307 Temporary Redirect。

3. 登录路由存在重复响应问题

在登录路由的第一个if分支中,你连续调用了res.status(401).json(...)和res.json(username),这会触发两次响应发送,Express会抛出错误,同时干扰正常的响应逻辑。

修正后的代码

服务端Session验证中间件(限定作用路径)

// 仅对/api/budget开头的受保护路由生效
app.use("/api/budget", function(req, res, next) {
  if (!req.session || !req.session.user) {
    // 使用标准302临时重定向
    return res.redirect('http://localhost:3000/index.html');
    // 如果是纯API场景,推荐返回401状态码由客户端处理重定向
    // return res.status(401).json({ message: "未授权,请登录" });
  }
  next();
});

服务端登录路由(修复重复响应)

app.post("/api/auth/signin", async (req, res) => {
  const username = req.body.username;
  const password = req.body.password;

  console.log(`Request JSON: ${JSON.stringify(req.body)}`);

  // 提前拦截空用户名/密码
  if (!username || !password) {
    return res.status(401).json({
      message: "用户名或密码不能为空",
    });
  }

  const userRequested = await db
    .collection("users")
    .findOne({ username: username });

  if (!userRequested) {
    return res.status(401).json({
      message: "用户不存在",
    });
  }

  if (userRequested.password === password) {
    req.session.user = userRequested;
    return res.status(200).json(userRequested);
  } else {
    return res.status(401).json({
      message: "用户名或密码错误",
    });
  }
});

客户端Fetch请求(优化逻辑+携带Cookie)

async beforeMount() {
  console.log("页面加载时执行");
  try {
    const res = await fetch("/api/budget/whoami", {
      method: "GET",
      credentials: "include" // 关键:携带session Cookie,否则服务端无法识别用户状态
    });

    if (res.status === 401 || res.status === 302) {
      window.location.replace("index.html");
      return;
    }

    if (!res.ok) {
      throw new Error("请求失败");
    }

    const user = await res.json();
    console.log("更新用户数据:", JSON.stringify(user));
    this.username = user.username;
    this.password = user.password;
    this.firstName = user.firstName;
    this.lastName = user.lastName;
  } catch (err) {
    console.error("获取用户数据失败:", err);
    window.location.replace("index.html");
  }
}

额外注意事项

  • Postman测试时,需开启自动保存Cookie功能,否则服务端无法识别session状态;
  • 纯API接口场景下,优先返回401 Unauthorized状态码,由客户端主动处理重定向,更符合RESTful规范。

内容的提问来源于stack exchange,提问作者Eminent Emperor Penguin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:43:16