Express-session会话管理:ERR_HTTP_HEADERS_SENT错误及跳转异常解决
基于express-session的会话管理应用问题排查与解决
问题背景
开发基于express-session的会话管理应用,需求为:用户会话存在时跳转至dashboard页面,否则跳转至登录页。开发过程中先后遇到两个问题:注册时触发ERR_HTTP_HEADERS_SENT错误,修复后又出现/dashboard路由无限302跳转的情况。
初始错误:ERR_HTTP_HEADERS_SENT
注册操作时多次弹出以下错误:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at new NodeError (node:internal/errors:372:5) at ServerResponse.setHeader (node:_http_outgoing:576:11) at ServerResponse.header (/home/deep/Node_API/node_modules/express/lib/response.js:794:10) at ServerResponse.location (/home/deep/Node_API/node_modules/express/lib/response.js:915:15) at ServerResponse.redirect (/home/deep/Node_API/node_modules/express/lib/response.js:953:18) at sessionChecker (/home/deep/Node_API/server/middlewares/session.middleware.js:5:13) at Layer.handle [as handle_request] (/home/deep/Node_API/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/home/deep/Node_API/node_modules/express/lib/router/index.js:328:13) at /home/deep/Node_API/node_modules/express/lib/router/index.js:286:9 at Function.process_params (/home/deep/Node_API/node_modules/express/lib/router/index.js:346:12)
应用核心代码
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 configuration */ 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: true, cookie: { secure: false, maxAge: 60000 * 5 }, saveUninitialized: true, }) ); app.use((req, res, next) => { if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } return next(); }); app.get("/", sessionChecker, (req, res) => { res.redirect("/login"); }); app.set("views", path.resolve(__dirname, "../views")); app.set("view engine", "ejs"); app.use(express.static("../public")); app.use("/", sessionChecker, api); // app.use(responseHandlerMiddleware); 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"); } }); module.exports = server;
会话检查中间件代码
const sessionChecker = (req, res, next) => { console.log("sesson ---------", req.session); console.log("cookies ---------", JSON.stringify(req.cookies)); if (req.session.user && req.cookies.user_sid) { res.redirect("/dashboard"); } else { return next(); } };
中间件修改后的新问题
修改会话检查中间件修复了ERR_HTTP_HEADERS_SENT错误,但注册完成后出现/dashboard路由无限302跳转的情况,且该路由的处理函数从未被执行:
GET /dashboard 302 246.426 ms - 64 GET /dashboard 302 247.005 ms - 64 GET /dashboard 302 245.620 ms - 64 GET /dashboard 302 247.940 ms - 64 GET /dashboard 302 246.114 ms - 64
用户注册控制器代码
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"); } });
最终解决方案
排查后发现问题根源:在需要渲染页面的场景下,错误使用了res.redirect而非res.render。将所有涉及页面渲染的res.redirect替换为res.render后,所有问题均得到解决。
内容的提问来源于stack exchange,提问作者Deep Mandal
相关产品推荐
相关产品推荐

