如何在Express中添加中间件捕获所有成功请求并记录用户活动?
解决API用户活动记录的中间件问题
针对你遇到的困境——前置中间件拿不到请求处理结果、后置中间件因响应结束无法执行,这里提供一个精准的解决方案,利用Express的响应事件监听来实现完整的用户活动记录:
核心思路
在前置中间件里监听响应的finish事件(该事件会在响应完全发送给客户端后触发),同时在路由处理函数中把请求的成功/失败状态、错误信息存入Express的本地变量res.locals,最后在finish回调里统一将这些信息写入Mongo集合。
具体实现步骤
1. 编写前置日志中间件
把这个中间件放在所有路由的最前面,用于初始化活动记录并监听响应结束:
app.use((req, res, next) => { // 初始化活动记录的基础信息 const userActivity = { method: req.method, path: req.path, clientIp: req.ip, timestamp: new Date(), userId: req.user?.id || null // 有用户认证的话,替换成你的用户ID字段 }; // 监听响应finish事件:响应发送完成后触发 res.on('finish', async () => { // 通过响应状态码判断请求是否成功 userActivity.statusCode = res.statusCode; userActivity.isSuccess = res.statusCode >= 200 && res.statusCode < 300; // 读取路由处理时存入的错误信息(如果有) userActivity.errorMessage = res.locals.activityError || null; // 写入Mongo集合,用异步操作避免阻塞主流程 try { await UserActivityModel.create(userActivity); // 替换成你的Mongo模型 } catch (err) { console.error('记录用户活动失败:', err); } }); next(); // 继续执行后续路由处理 });
2. 在路由处理中传递状态信息
在每个路由函数里,不管请求成功还是失败,都把对应的信息存入res.locals:
// 示例:成功的路由处理 app.get('/api/user/profile', async (req, res) => { try { const profile = await getUserProfile(req.user.id); res.status(200).json(profile); // 发送响应,后续会触发finish事件 } catch (err) { // 把错误信息存入locals,供中间件读取 res.locals.activityError = err.message; res.status(500).json({ error: '获取用户信息失败' }); } }); // 示例:参数错误的路由处理 app.post('/api/user/login', (req, res) => { const { username, password } = req.body; if (!username || !password) { res.locals.activityError = '用户名或密码为空'; return res.status(400).json({ error: '参数错误' }); } // 后续登录逻辑... });
3. 关键注意事项
- 必须调用响应结束方法:
res.send()、res.json()、res.end()是结束HTTP响应的必要操作,不能只设置res.statusCode,否则请求会一直处于挂起状态。 - 优先用
finish事件:它表示响应已成功发送给客户端;close事件可能在连接意外断开时触发,适合补充记录异常断开的场景。 - 异步操作不阻塞主流程:写入Mongo时用
async/await或Promise,即使写入失败也不要影响主响应的正常返回。
这个方案既保留了你原本路由中res.send()的使用习惯,又能完整记录每个请求的处理结果,完美解决你的问题。
内容的提问来源于stack exchange,提问作者THpubs
相关产品推荐
相关产品推荐

