Express中间件无法记录JSON请求?求技术排查方案
问题根源
你遇到的核心问题是请求体流只能被读取一次:
- 发送合法JSON格式的POST请求时,
express.json()(或body-parser.json())中间件会优先完整读取请求体流并解析为req.body,此时流已被耗尽。如果你的Morgan中间件在express.json()之后执行,尝试读取请求体时会获取不到内容,导致你误以为请求没被记录。 - 非JSON格式的DELETE请求:
express.json()尝试解析失败,不会完全消耗请求体流,后续Morgan能正常读取流内容完成记录。 - 格式错误的JSON DELETE请求:
express.json()解析失败后直接触发错误处理流程,返回JSON语法错误响应,此时请求体流可能还未被完全消耗,或者错误响应触发了Morgan的日志记录逻辑。
解决方案
要解决这个问题,需要先缓存请求体的原始内容,让express.json()和Morgan都能访问到请求数据,具体步骤如下:
- 添加请求体缓存中间件
在所有解析中间件(如express.json())之前,添加一个自定义中间件,将原始请求体缓存到req.rawBody中:
app.use((req, res, next) => { let rawBody = ''; req.on('data', (chunk) => { rawBody += chunk.toString(); }); req.on('end', () => { req.rawBody = rawBody; next(); }); });
- 配置
express.json()
之后再启用JSON解析中间件,此时它会从缓存的原始内容中解析,不会影响后续Morgan的读取:
app.use(express.json());
- 修改Morgan配置使用缓存的请求体
自定义Morgan的token或日志格式,直接使用req.rawBody来获取原始请求体,并存入全局变量:
const morgan = require('morgan'); // 自定义请求体token morgan.token('raw-body', (req) => req.rawBody || ''); app.use(morgan((tokens, req, res) => { // 将请求信息存入全局变量 global.requests = global.requests || []; global.requests.push({ method: tokens.method(req, res), url: tokens.url(req, res), body: tokens['raw-body'](req, res), status: tokens.status(req, res), timestamp: tokens.date(req, res, 'iso') }); // 可选:返回日志字符串用于控制台输出 return [ tokens.date(req, res, 'iso'), tokens.method(req, res), tokens.url(req, res), tokens.status(req, res), tokens['raw-body'](req, res) ].join(' | '); }));
额外注意事项
- 全局变量
global.requests仅适合本地测试,生产环境建议使用内存数据库(如Redis)或文件存储,避免进程重启丢失数据。 - 如果你的项目中使用了其他解析中间件(如
express.urlencoded()),同样需要保证缓存中间件在它们之前执行。
内容的提问来源于stack exchange,提问作者RudeusGreyrat
相关产品推荐
相关产品推荐

