在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 } }); });
你之前错误的原因解析
- "next不是函数"错误:你把需要
next参数的中间件当成普通函数调用了(比如在路由处理函数里直接setTokenCookie(req, res)),此时函数的第三个参数next未传递,调用next()自然会报错。 - 删除next后收不到Cookie:大概率是调用完
setTokenCookie后没有发送响应(比如没写res.json()),或者Cookie的secure参数在开发环境设为true(HTTP环境下浏览器会拒绝接收)。
内容的提问来源于stack exchange,提问作者M.KH
相关产品推荐
相关产品推荐

