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

NestJS中间件抛错时异常过滤器内自定义logger为undefined

问题根因

三个直接问题导致中间件抛错时this.logger.fatal为undefined:

  • 依赖注入错误:你在异常过滤器中注入的是LogController,但实际实现fatal/error日志方法的是LogService。Controller本身是处理HTTP路由请求的类,默认不会挂载日志方法。非中间件场景下能偶然运行,是因为路由请求处理阶段Controller实例刚好完成初始化,但中间件执行时机早于路由层Controller实例化,forwardRef无法拿到尚未初始化的Controller实例,最终this.logger是空值,对应方法自然为undefined。
  • 全局过滤器绑定方式错误:如果在main.ts中通过app.useGlobalFilters(new AllExceptionsFilter())手动实例化过滤器,过滤器的依赖不会走Nest依赖注入容器管理,会直接出现依赖丢失。
  • 代码存在语法错误:status变量的三元表达式没有写全默认值、LogService的方法中多写了冗余的闭合括号,会触发额外的运行时报错。
修复方案

按以下步骤调整即可解决问题:

  1. 调整依赖关系:把日志核心逻辑全部下沉到LogService,异常过滤器直接注入LogService,移除对LogController的依赖和不必要的forwardRef,从根源消除循环依赖。
  2. 用Nest官方提供的DI方式注册全局异常过滤器,不要手动new实例,保证所有依赖的初始化时序正确。
  3. 修复现有代码的语法错误,补全缺失的逻辑分支。
  4. (可选兜底)在应用入口最外层加一层中间件错误捕获,保证中间件阶段抛出的异常100%流转到全局过滤器。
修正后代码

exception.filter.ts

import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpArgumentsHost } from '@nestjs/common';
import { Response } from 'express';
import { LogService } from './log.service'; // 替换为你项目中LogService的实际路径

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly logger: LogService) {}

  catch(exception: HttpException | Error, host: ArgumentsHost): void {
    const ctx: HttpArgumentsHost = host.switchToHttp();
    const response: Response = ctx.getResponse();
    // 补全status默认值,非HttpException类错误默认返回500状态码
    const status = exception instanceof HttpException ? exception.getStatus() : 500;
    
    if (status >= 500) {
      this.logger.fatal(exception.message, exception.stack?.toString() || '');
    }

    if (status >= 400 && status < 500) {
      this.logger.error(exception.message, exception.stack?.toString() || '');
    }

    AllExceptionsFilter.handleResponse(response, exception, status);
  }

  private static handleResponse(res: Response, exception: HttpException | Error, status: number) {
    const responseBody = exception instanceof HttpException 
      ? exception.getResponse() 
      : { message: exception.message, statusCode: status };
    res.status(status).json(responseBody);
  }
}

app.module.ts(注册全局过滤器)

不要在main.ts中手动new过滤器实例,在根模块通过APP_FILTER令牌注册:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionsFilter } from './exception.filter';
import { LogModule } from './log/log.module'; // 替换为你项目中日志模块的实际路径

@Module({
  imports: [LogModule],
  providers: [
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
  ],
})
export class AppModule {}

log.service.ts(修复语法错误)

import { Injectable } from '@nestjs/common';

@Injectable()
export class LogService {
  error(message: any, stack?: string) {
    console.log(`message::${message} - stack::${stack}`);
  }

  fatal(message: any, stack?: string) {    
    console.log(`message::${message} - stack::${stack}`);
  }
}

main.ts(可选兜底配置)

在所有中间件注册前加一层错误转发中间件,保证中间件异常不丢失:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// 引入你的认证中间件
// import { AuthenticationMiddleware } from './auth.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 最外层错误兜底
  app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
    next(err);
  });
  // 再注册业务中间件
  // app.use(new AuthenticationMiddleware().use);
  await app.listen(3000);
}
bootstrap();
注意事项
  • 不要让全局公共依赖的Service反向依赖Controller,Controller只负责处理HTTP请求,核心逻辑全部下沉到Service层,避免循环依赖。
  • 所有全局的过滤器、守卫、拦截器如果有依赖注入需求,必须通过APP_FILTER/APP_GUARD/APP_INTERCEPTOR令牌在模块中注册,不要手动实例化,否则会出现依赖丢失、初始化时序错误。

内容的提问来源于stack exchange,提问作者Xuân Bình

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 18:06:27