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

求助: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:15:56