为何需在Express-Session中间件后访问session对象才能正常工作?
问题描述
TLDR:
为什么我必须在app.js的session中间件之后添加这段访问session对象的代码,才能让Express-Session正常工作?不加的话会话不会保存,也不会设置cookie。
app.use((req, res, next) => { req.session.init = "init"; next(); });
编辑: 我认为这不是cookie设置的问题,因为不加这段代码时会话也不会保存,加上后会话和cookie都能正常工作。
原始问题
我正在用Express-Session将会话存储到MySQL数据库里,不知道为什么它突然不保存会话也不返回cookie了。我的Express日志如下:
express:router dispatching POST /auth/login +18s express:router query : /auth/login +0ms express:router expressInit : /auth/login +1ms express:router session : /auth/login +0ms express-session no SID sent, generating session +1ms express:router <anonymous> : /auth/login +1ms express:router jsonParser : /auth/login +0ms express:router urlencodedParser : /auth/login +1ms express:router trim prefix (/auth) from url /auth/login +0ms express:router router /auth : /auth/login +1ms express:router dispatching POST /login +0ms
但添加上述代码后,会话就能保存,也会返回cookie。我的相关代码如下:
const options = { host: process.env.DB_HOST, port: process.env.DB_PORT, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE }; const sessionStore = new MySQLStore(options); app.use(session({ name: process.env.SESSION_NAME, secret: process.env.SESSION_SECRETS, store: sessionStore, resave: false, saveUninitialized: false, cookie: { secure: false, httpOnly: true, sameSite: 'strict', maxAge:600000 } })); // 这段代码让Express-Session正常工作 app.use((req, res, next) => { req.session.init = "init"; next(); }); require('./startup/headers')(app); require('./startup/routes')(app); app.use((error, req, res, next) => { const status = error.statusCode || 500; res.status(status).json({ message: `Error`}); });
此时的Express日志如下(注意末尾的express-session相关日志):
express:router dispatching POST /auth/login +58ms express:router query : /auth/login +1ms express:router expressInit : /auth/login +2ms express:router session : /auth/login +1ms express-session no SID sent, generating session +5ms express:router <anonymous> : /auth/login +4ms express:router <anonymous> : /auth/login +0ms express:router jsonParser : /auth/login +3ms express:router urlencodedParser : /auth/login +3ms express:router trim prefix (/auth) from url /auth/login +1ms express:router router /auth : /auth/login +1ms express:router dispatching POST /login +0ms express-session saving 3FOu73E3BHGVl0Rw-KtrCOjjqcDZlxRY +31ms express-session set-cookie session-cookie=s%3A3FOu73E3BHGVl0Rw-KtrCOjjqcDZlxRY.ucs49AVEgJFmJ1b5CeDuICqIihbDeNa294ImU4XC2lg; Path=/; Expires=Mon, 23 Jan 2023 11:29:39 GMT; HttpOnly; SameSite=Strict +9ms express-session split response +8ms
我在文档里没找到相关说明,希望有人能解释这种行为。
原因解释
核心原因是你配置了saveUninitialized: false,这个参数的作用是拒绝保存未初始化的会话。
在Express-Session的逻辑里:
- 当请求进来时,中间件会生成一个空的会话对象,但此时这个会话属于“未初始化”状态——没有任何属性被修改或添加。
- 因为
saveUninitialized设为false,中间件会跳过这个会话的保存流程,也不会向客户端发送Set-Cookie响应头。
你添加的req.session.init = "init"代码,本质是对会话对象做了修改,把它从“未初始化”转为“已初始化”状态。这时Express-Session就会触发会话保存逻辑,同时向客户端设置cookie。
如果你的登录接口原本就会修改req.session(比如存储用户ID、权限信息),那根本不需要这段额外代码。但如果当前登录逻辑里没有对会话做任何修改,就会出现会话不保存、不设cookie的情况。
补充:resave: false的作用是仅当会话被修改时才重新保存,和本次问题关联不大,但saveUninitialized是触发问题的关键配置。
内容的提问来源于stack exchange,提问作者NickW

