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

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

问题根源

  1. 跳转循环原因:
    全局匿名中间件和/dashboard路由的sessionChecker逻辑冲突:

    • 注册成功后,用户session已设置,全局中间件检测到后重定向到/dashboard
    • /dashboard路由的sessionChecker又检测到用户已登录,再次重定向到/dashboard,形成无限循环
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:15:43