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

如何在NestJS中间件中获取500响应的响应体?

解决方案:在NestJS中间件中获取500状态码的响应体

因为Express/NestJS的响应采用流式输出机制,默认不会将响应体存储到response对象上,所以你需要主动拦截响应发送的核心方法,提前把响应体保存下来,才能在finish事件中获取。


方案一:修改中间件,拦截响应发送方法

重写response.send和response.json方法,在实际发送响应前将响应体挂载到response对象上,后续就能在finish回调中读取:

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(request: Request, response: Response, next: NextFunction): void {
    const { ip, method, originalUrl } = request;
    const userAgent = request.get("user-agent") || "";

    // 保存原生send方法,并重写
    const originalSend = response.send;
    response.send = function(body) {
      // 将响应体挂载到response对象(用类型断言绕过TS类型检查)
      (response as any).body = body;
      // 调用原生方法发送响应
      return originalSend.call(this, body);
    };

    // 额外拦截json方法(如果接口会用response.json返回数据)
    const originalJson = response.json;
    response.json = function(body) {
      (response as any).body = body;
      return originalJson.call(this, body);
    };

    response.on("finish", () => {
      const { statusCode } = response;
      const contentLength = response.get("content-length");

      const basicRequestMetaInfo = {
        method,
        originalUrl,
        statusCode,
        // 其他自定义字段...
      };

      if (this.isErroneousStatusCode(statusCode)) {
        const additionalRequestMetaInfo = {
          body: (response as any).body, // 现在可以拿到响应体了
        };

        this.winstonLoggerErrorLevel.error({
          ...basicRequestMetaInfo,
          ...additionalRequestMetaInfo,
        });
        return;
      }
      this.winstonLoggerInfoLevel.info(basicRequestMetaInfo);
    });
    next();
  }

  private isErroneousStatusCode(statusCode: number): boolean {
    return statusCode >= 500;
  }
}

方案二:使用NestJS拦截器(更推荐)

NestJS的拦截器天生支持在响应发送前后处理数据,比中间件更适配这类场景,代码更简洁:

@Injectable()
export class ErrorLoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const { method, originalUrl } = request;

    return next.handle().pipe(
      // 正常响应时记录日志
      tap((responseBody) => {
        const response = context.switchToHttp().getResponse();
        this.winstonLoggerInfoLevel.info({
          method,
          originalUrl,
          statusCode: response.statusCode,
        });
      }),
      // 异常响应时捕获并记录
      catchError((error) => {
        const response = context.switchToHttp().getResponse();
        this.winstonLoggerErrorLevel.error({
          method,
          originalUrl,
          statusCode: error.getStatus ? error.getStatus() : 500,
          body: error.getResponse ? error.getResponse() : error.message,
        });
        return throwError(() => error);
      }),
    );
  }
}

注册拦截器的方式:

  • 在模块的providers数组中配置全局拦截器
  • 用@UseInterceptors(ErrorLoggingInterceptor)装饰器加到指定控制器/方法上

注意事项

  • 若接口返回大文件流,不建议保存响应体,会占用大量内存,仅针对JSON/文本格式的接口响应处理即可
  • 可以通过扩展Express的类型定义,避免每次使用(response as any)的类型断言

内容的提问来源于stack exchange,提问作者DanilMakarov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:39:57