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

在Express控制器中调用中间件:JWT Cookie复用问题排查

正确封装Express中JWT Cookie复用逻辑的方案

你遇到的问题核心是混淆了Express中间件和普通工具函数的调用方式,以下是两种可靠的实现方案:

方案一:封装为普通工具函数(推荐,灵活度高)

这种方式适合在路由处理函数内部直接调用,不需要依赖next参数,逻辑清晰且复用性强。

封装代码

const jwt = require('jsonwebtoken');

// 工具函数:接收请求对象、响应对象、用户实例,生成JWT并设置Cookie
function setTokenCookie(req, res, user) {
  // 生成JWT(同步方法,无需await)
  const token = jwt.sign(
    { userId: user._id },
    process.env.JWT_SECRET,
    { expiresIn: '7d' } // 7天有效期
  );

  // 设置Cookie,根据环境配置安全参数
  res.cookie('token', token, {
    httpOnly: true, // 禁止前端JS读取,防XSS
    secure: process.env.NODE_ENV === 'production', // 生产环境仅HTTPS传输
    sameSite: 'strict', // 防CSRF
    maxAge: 7 * 24 * 60 * 60 * 1000 // 有效期毫秒数
  });
}

module.exports = setTokenCookie;

路由中使用

const setTokenCookie = require('./utils/setTokenCookie');
const User = require('./models/User');

router.post('/login', async (req, res) => {
  try {
    // 从MongoDB查询用户
    const user = await User.findOne({ email: req.body.email });
    if (!user) {
      return res.status(401).json({ message: '用户不存在' });
    }

    // 调用工具函数设置Cookie
    setTokenCookie(req, res, user);

    // 必须发送响应,否则浏览器会一直等待
    res.status(200).json({
      message: '登录成功',
      user: { id: user._id, email: user.email }
    });
  } catch (err) {
    res.status(500).json({ message: '服务器错误' });
  }
});

方案二:封装为Express中间件(适合路由链场景)

如果你的业务逻辑需要在多个中间件链式调用中处理(比如先查询用户,再设置Cookie,最后返回响应),可以封装为标准中间件。

封装代码

const jwt = require('jsonwebtoken');

// 中间件:假设req.user已由前置中间件查询并挂载
function setTokenCookie(req, res, next) {
  // 校验用户是否存在
  if (!req.user) {
    return next(new Error('未获取到用户信息'));
  }

  const token = jwt.sign(
    { userId: req.user._id },
    process.env.JWT_SECRET,
    { expiresIn: '7d' }
  );

  res.cookie('token', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 7 * 24 * 60 * 60 * 1000
  });

  // 调用next,让后续路由处理函数继续执行
  next();
}

module.exports = setTokenCookie;

路由中使用

const setTokenCookie = require('./middleware/setTokenCookie');
const User = require('./models/User');

// 前置中间件:查询用户并挂载到req.user
async function findUser(req, res, next) {
  try {
    const user = await User.findOne({ email: req.body.email });
    if (!user) {
      return res.status(401).json({ message: '用户不存在' });
    }
    req.user = user;
    next();
  } catch (err) {
    next(err);
  }
}

// 路由链:先查用户,再设Cookie,最后返回响应
router.post('/login', findUser, setTokenCookie, (req, res) => {
  res.status(200).json({
    message: '登录成功',
    user: { id: req.user._id, email: req.user.email }
  });
});

你之前错误的原因解析

  1. "next不是函数"错误:你把需要next参数的中间件当成普通函数调用了(比如在路由处理函数里直接setTokenCookie(req, res)),此时函数的第三个参数next未传递,调用next()自然会报错。
  2. 删除next后收不到Cookie:大概率是调用完setTokenCookie后没有发送响应(比如没写res.json()),或者Cookie的secure参数在开发环境设为true(HTTP环境下浏览器会拒绝接收)。

内容的提问来源于stack exchange,提问作者M.KH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 18:57:43