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
相关产品推荐
相关产品推荐

