求助:express-session保存后无法成功读取的问题
为什么我的express-session无法持久化?
问题场景
我在payment_router.js中设置了会话数据,但在另一个路由文件paymentSuccessful_Router.js中读取时,会话为空,无法获取之前保存的advertFormData。
1. payment_router.js 代码
const express = require('express'); const router = express.Router(); const { initiatePayment } = require('../routes/paymentProcessor'); const { v4: uuidv4 } = require('uuid'); require('dotenv').config(); // 处理支付发起的路由 router.post('/pay', async (req, res) => { req.session.advertFormData = { advertName: req.body.advertName, chosenPackageName: req.body.chosenPackageName, daterange: req.body.daterange, // ...省略其他字段 }; req.session.save((err) => { if (err) { console.error('Error saving session:', err); } console.log('Session saved successfully with advertFormData 01:', req.session.advertFormData); }); console.log('###################:-----> 00 ', req.sessionID); console.log('###################:-----> 11 ', req.session); console.log('###################:-----> 22 ', req.session.advertFormData); // ...省略其他代码 });
2. 会话保存成功的日志输出
[1] ###################:-----> 00 ZU7Nix6vTjdByILuv-SAuJ-vI7YGcGHj [1] ###################:-----> 11 Session { [1] cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }, [1] passport: { user: '62fb82ac74bcc4de938c3592' }, [1] flash: {}, [1] advertFormData: { [1] advertName: 'Testing payments', [1] chosenPackageName: 'MVP', [1] daterange: '01/01/2025 - 02/01/2025', // ...省略其他字段 [1] ###################:-----> 22 { [1] advertName: 'Testing payments', [1] chosenPackageName: 'MVP', [1] daterange: '01/01/2025 - 02/01/2025', [1] locationArray: [ [1] '[{"zone":"Siaya, Kenya","lat":"0.05615","lng":"34.28829"},{"zone":"Surat (Soorat), Gujarat, India","lat":"21.1859","lng":"72.83662"}]', [1] '' [1] ], [1] CTAButtonName: 'Buy now', // ...省略其他字段 Session saved successfully with advertFormData 01: { [1] advertName: 'Testing payments', [1] chosenPackageName: 'MVP', [1] daterange: '01/01/2025 - 02/01/2025', [1] locationArray: [ [1] '[{"zone":"Siaya, Kenya","lat":"0.05615","lng":"34.28829"},{"zone":"Surat (Soorat), Gujarat, India","lat":"21.1859","lng":"72.83662"}]', [1] '' // ...省略其他字段
3. paymentSuccessful_Router.js 代码
const express = require('express'); const router = express.Router(); router.get('/successful', (req, res) => { const advertFormData = req.session.advertFormData || { advertName: '', companyEmail: '', chosenPackageName: '', daterange: '', locationArray: [], CTAButtonName: '', CTA_Link: '', dayArray: [], timesArraySettingToSave: [], phone: '', amount: '', scheduledDays: '', scheduledDays_andTimes: '', }; console.log('Advert Form Data: 0', advertFormData); console.log('Advert Form Data: 1', req.session.advertFormData); // ...省略其他代码 });
4. 读取会话失败的日志输出
[1] Advert Form Data: 0 { [1] advertName: '', [1] companyEmail: '', [1] chosenPackageName: '', [1] daterange: '', [1] locationArray: [], [1] CTAButtonName: '', [1] CTA_Link: '', [1] dayArray: [], [1] timesArraySettingToSave: [], [1] phone: '', [1] amount: '', [1] scheduledDays: '', [1] scheduledDays_andTimes: '' [1] } [1] Advert Form Data: 1 undefined
5. app.js 中的会话配置
const session = require('express-session'); // Session configuration app.use( session({ secret: process.env.SESSION_SECRET, // resave: false, // saveUninitialized: false, // store: MongoStore.create({ // mongoUrl: process.env.MONGODB_URI, // ttl: 14 * 24 * 60 * 60, // autoRemove: 'native', // }), // cookie: { // secure: process.env.NODE_ENV === 'production', // httpOnly: true, // maxAge: 14 * 24 * 60 * 60 * 1000, // }, // name: 'driverSession', }) ); // CORS配置 app.use(cors({ origin: 'http://localhost:4000', credentials: true, // 允许共享cookie/会话 })); app.use(passport.session());
6. 触发重定向的 paymentCallbackRouter.js 代码
router.get('/callback', async (req, res) => { console.log('## Received callback request:', req.query); const { OrderTrackingId, OrderMerchantReference } = req.query; if (!OrderTrackingId || !OrderMerchantReference) { console.error('Missing callback parameters:', req.query); return res.status(400).json({ error: 'Invalid callback request' }); } console.log('## Processing payment callback for:', { OrderTrackingId, OrderMerchantReference }); try { const isPaymentSuccessful = true; // 模拟支付验证成功 req.session.paymentStatus = isPaymentSuccessful ? 'success' : 'failure'; req.session.orderTrackingId = OrderTrackingId; if (isPaymentSuccessful) { console.log('Payment verification successful:', { OrderTrackingId }); return res.redirect(`${AppURL}/paymentSuccessful/successful?OrderTrackingId=${OrderTrackingId}`); } else { console.warn('Payment verification failed:', { OrderTrackingId }); return res.redirect(`${AppURL}/paymentSuccessful/failure?OrderTrackingId=${OrderTrackingId}`); } } catch (error) { console.error('Error processing callback:', error.message); return res.status(500).json({ error: 'Callback processing failed' }); } });
问题原因及解决方法
1. 默认内存存储的局限性
当前使用的是express-session默认的内存存储,仅适用于开发环境:服务器重启、进程结束后会话会丢失;且支付网关的回调请求不携带用户会话Cookie,导致回调时生成新的空会话,重定向后自然读取不到之前的数据。
解决:
启用持久化存储(如MongoDB的MongoStore),取消app.js中相关配置的注释:
const MongoStore = require('connect-mongo'); // 先执行 npm install connect-mongo 安装依赖 app.use( session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, store: MongoStore.create({ mongoUrl: process.env.MONGODB_URI, ttl: 14 * 24 * 60 * 60, // 会话有效期14天 autoRemove: 'native', }), cookie: { secure: process.env.NODE_ENV === 'production', // 生产环境必须启用HTTPS httpOnly: true, maxAge: 14 * 24 * 60 * 60 * 1000, // Cookie有效期14天 }, name: 'driverSession', }) );
2. 支付回调丢失会话Cookie
支付网关的回调请求是直接发送到服务器的,不会携带用户浏览器中的会话Cookie,此时req.session是新的空会话,重定向后无法读取之前保存的advertFormData。
解决:
- 不要依赖会话传递表单数据:发起支付时,生成唯一订单参考号,将
advertFormData与该参考号绑定存入数据库;回调时通过OrderMerchantReference查询数据库获取表单数据,再传递给/successful路由。 - 敏感数据避免通过URL传递,如需传递需加密处理。
3. 异步操作未等待会话保存完成
在/pay路由中,调用req.session.save()后未等待保存完成就可能执行后续操作(如跳转支付网关),可能导致会话未持久化。
解决:
将req.session.save()包装为Promise,等待保存完成后再继续:
await new Promise((resolve, reject) => { req.session.save((err) => { if (err) { console.error('Error saving session:', err); reject(err); } else { console.log('Session saved successfully with advertFormData 01:', req.session.advertFormData); resolve(); } }); });
内容的提问来源于stack exchange,提问作者SirBT
相关产品推荐
相关产品推荐

