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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 02:36:18