Express中res.redirect多次触发却不跳转及sessionChecker无日志问题
Express应用跳转循环与Session中间件无输出问题
问题现象
- 注册成功后调用
res.redirect("/dashboard"),页面无法完成跳转,日志反复输出GET /dashboard 302 255.451 ms - 64后停止,陷入无限跳转循环 sessionChecker中间件中的console.log完全没有输出,说明该中间件未被正确执行
相关代码
Express主应用代码
const https = require("https"); const http = require("http"); const path = require("path"); const express = require("express"); const cookieParser = require("cookie-parser"); const dotenv = require("dotenv"); const cors = require("cors"); const helmet = require("helmet"); const logger = require("morgan"); const compression = require("compression"); const session = require("express-session"); const pgSession = require("connect-pg-simple")(session); const api = require("../server/routes"); const pool = require("../config/database"); dotenv.config({ path: require("find-config")(".env") }); const configurations = { production: { ssl: true, port: process.env.PORT, hostname: "" }, development: { ssl: false, port: process.env.PORT, hostname: process.env.STAGE_HOSTNAME, }, }; const environment = process.env.NODE_ENV || "development"; const config = configurations[environment]; const { responseHandlerMiddleware, sessionChecker, } = require("../server/middlewares"); const app = express(); var credentials = { // key: fs.readFileSync("/etc/letsencrypt/live/myresorts.in/privkey.pem"), // cert: fs.readFileSync("/etc/letsencrypt/live/myresorts.in/fullchain.pem"), }; let server = config.ssl ? https.createServer(credentials, app) : http.createServer(app); app.use(cors()); app.options("*", cors()); app.use(helmet()); app.use(compression()); app.use(logger("dev")); app.use(express.json({ limit: "20mb" })); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.set("trust proxy", 1); app.use( session({ store: new pgSession({ pool, tableName: "session", }), name: "user_sid", secret: process.env.SESSION_SECRET, resave: false, cookie: { secure: false, maxAge: 600000000000 }, saveUninitialized: false, }) ); app.set("views", path.resolve(__dirname, "../views")); app.set("view engine", "ejs"); app.use(express.static("../public")); app.get("/", sessionChecker, (req, res) => { res.redirect("/login"); }); app.use((req, res, next) => { if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } else { return next(); } }); app.route("/login").get(sessionChecker, (req, res) => { res.render("pages/login"); }); app.route("/register").get(sessionChecker, (req, res) => { res.render("pages/register"); }); app.route("/dashboard").get(sessionChecker, (req, res) => { if (req.session.user && req.cookies.user_sid) { res.render("pages/dashboard"); } else { res.redirect("/login"); } }); app.use("/", sessionChecker, api); module.exports = server;
sessionChecker中间件代码
const sessionChecker = (req, res, next) => { console.log("sesson ---------", req.session.user); console.log("cookies ---------", JSON.stringify(req.cookies)); if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } else { return next(); } };
registerUser处理函数代码
const catchAsync = require("../utilities/catchAsync"); const { generatePassword, validatePassword, } = require("../utilities/passwordUtilities"); const pool = require("../../config/database"); const registerUser = catchAsync(async (req, res, next) => { const { email, password } = req.body; const user = await pool.query("select * from find_user_by_email($1)", [ email, ]); if (user.rowCount) { res.error = "User already exists"; return next(500); } const saltHash = generatePassword(password); const response = await pool.query("select * from create_user($1, $2, $3)", [ email, saltHash.salt, saltHash.hash, ]); if (response.rowCount) { req.session.user = { email: response.rows[0].create_user }; res.redirect("/dashboard"); } else { res.redirect("/register"); } });
问题根源
跳转循环原因:
全局匿名中间件和/dashboard路由的sessionChecker逻辑冲突:- 注册成功后,用户session已设置,全局中间件检测到后重定向到
/dashboard /dashboard路由的sessionChecker又检测到用户已登录,再次重定向到/dashboard,形成无限循环
- 注册成功后,用户session已设置,全局中间件检测到后重定向到
sessionChecker无输出原因:
全局匿名中间件的执行顺序在所有路由之前,当用户已登录时会直接重定向,不会走到后续路由的sessionChecker;未登录时直接调用next(),但sessionChecker挂载的路由逻辑被绕过,导致中间件从未执行。
解决方案
1. 移除冲突的全局匿名中间件
删除这段重复逻辑的代码:
app.use((req, res, next) => { if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } else { return next(); } });
2. 重构sessionChecker中间件逻辑
修改为只拦截未登录用户访问受保护路由,避免重定向循环:
const sessionChecker = (req, res, next) => { console.log("session ---------", req.session.user); console.log("cookies ---------", JSON.stringify(req.cookies)); // 未登录时重定向到登录页,已登录则继续执行 if (!req.session.user || !req.cookies.user_sid) { res.redirect("/login"); } else { return next(); } };
3. 调整路由的中间件挂载策略
- 公开路由(登录、注册):添加已登录自动跳转到dashboard的逻辑,不挂载
sessionChecker - 受保护路由(dashboard、API):挂载
sessionChecker验证登录状态
// 根路由:已登录跳dashboard,未登录跳login app.get("/", (req, res) => { if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } else { res.redirect("/login"); } }); // 公开路由:未登录可访问,已登录自动跳转 app.route("/login").get((req, res) => { if (req.session.user && req.cookies.user_sid) { return res.redirect("/dashboard"); } res.render("pages/login"); }); app.route("/register").get((req, res) => { if (req.session.user && req.cookies.user_sid) { return res.redirect("/dashboard"); } res.render("pages/register"); }); // 受保护路由:必须登录才能访问 app.route("/dashboard").get(sessionChecker, (req, res) => { res.render("pages/dashboard"); }); // API路由受保护 app.use("/", sessionChecker, api);
4. 确保Session持久化
注册成功后显式保存session,避免重定向时session未写入的问题:
const registerUser = catchAsync(async (req, res, next) => { // ... 原有代码 if (response.rowCount) { req.session.user = { email: response.rows[0].create_user }; // 显式保存session await req.session.save(); res.redirect("/dashboard"); } else { res.redirect("/register"); } });
内容的提问来源于stack exchange,提问作者Deep Mandal
相关产品推荐
相关产品推荐

