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的方法中多写了冗余的闭合括号,会触发额外的运行时报错。
修复方案
按以下步骤调整即可解决问题:
- 调整依赖关系:把日志核心逻辑全部下沉到
LogService,异常过滤器直接注入LogService,移除对LogController的依赖和不必要的forwardRef,从根源消除循环依赖。 - 用Nest官方提供的DI方式注册全局异常过滤器,不要手动new实例,保证所有依赖的初始化时序正确。
- 修复现有代码的语法错误,补全缺失的逻辑分支。
- (可选兜底)在应用入口最外层加一层中间件错误捕获,保证中间件阶段抛出的异常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
相关产品推荐
相关产品推荐

