如何在NestJS的Logger中间件中获取服务器响应数据?
在NestJS中间件中获取Express响应数据的方法
Express原生的Response对象并没有直接提供获取响应体的属性,要在NestJS的Logger中间件中记录响应数据,需要通过劫持响应发送方法来实现,具体步骤如下:
1. 劫持res.send和res.json方法
Express通过res.send()或res.json()发送响应,我们可以重写这两个方法,在实际发送响应前把数据暂存到response.locals(Express用于存储请求级别的自定义数据)中,后续在close事件里读取。
2. 修改后的中间件代码
import { NextFunction, Request, Response } from 'express'; import { Injectable, Logger, NestMiddleware } from '@nestjs/common'; @Injectable() export class EcomWebLogerMiddleware implements NestMiddleware { private logger = new Logger(); use(request: Request, response: Response, next: NextFunction): void { const { method, url: path, query, params, body } = request; // 记录请求信息 this.logger.log( ` ${method} ${path} query: ${JSON.stringify(query)} params: ${JSON.stringify(params)} body: ${JSON.stringify(body)}`, 'FE Request' ); // 保存原始的send和json方法 const originalSend = response.send; const originalJson = response.json; // 劫持res.send方法 response.send = function (data) { response.locals.responseData = data; return originalSend.call(this, data); }; // 劫持res.json方法(处理JSON格式响应) response.json = function (data) { response.locals.responseData = data; return originalJson.call(this, data); }; // 在响应结束时记录响应信息 response.on('close', () => { const { statusCode } = response; const responseData = response.locals.responseData; // 处理响应数据,比如转成字符串(如果是对象的话) const formattedResponse = typeof responseData === 'object' ? JSON.stringify(responseData) : responseData; this.logger.log( `${method} ${statusCode} ${path} response: ${formattedResponse}`, 'FE Response' ); }); next(); } }
3. 备选方案:使用NestJS拦截器(Interceptor)
如果不需要在Express中间件层面实现,NestJS的拦截器更适合处理请求/响应日志,它能直接获取到控制器返回的响应数据,无需劫持Express方法:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { private logger = new Logger(); intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const request = context.switchToHttp().getRequest(); const { method, url: path, query, params, body } = request; // 记录请求 this.logger.log( ` ${method} ${path} query: ${JSON.stringify(query)} params: ${JSON.stringify(params)} body: ${JSON.stringify(body)}`, 'FE Request' ); const now = Date.now(); return next.handle().pipe( tap((responseData) => { // 记录响应 const statusCode = context.switchToHttp().getResponse().statusCode; this.logger.log( `${method} ${statusCode} ${path} response: ${JSON.stringify(responseData)} (${Date.now() - now}ms)`, 'FE Response' ); }), ); } }
说明
- 中间件方案:适合需要在Express底层处理日志的场景,兼容所有响应发送方式。
- 拦截器方案:更贴合NestJS的架构,能直接获取控制器返回的原始数据,无需处理Express的方法劫持。
内容的提问来源于stack exchange,提问作者Patrick Phan
相关产品推荐
相关产品推荐

