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

NestJS ExceptionFilter能否实现类似Spring Boot @RestControllerAdvice的多错误全局处理?

NestJS 实现类似 Spring Boot @RestControllerAdvice 的全局多异常处理

你完全没写错基础用法,但NestJS的ExceptionFilter其实有更灵活的姿势,完全能实现和Spring @RestControllerAdvice一样的多异常处理能力,甚至能覆盖包括非HttpException在内的所有全局错误。下面给你拆解两种核心实现方式:

1. 精准捕获指定的多种异常类型

和Spring里多个@ExceptionHandler对应,你可以给@Catch()装饰器传入多个异常类,这样一个Filter就能针对性处理不同的异常场景:

import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { HttpException, Logger } from '@nestjs/common';
import { Response, Request } from 'express';
// 导入你的自定义异常
import { DuplicateEmailException } from './exceptions/duplicate-email.exception';
import { MethodArgumentNotValidException } from './exceptions/validation.exception';

@Catch(HttpException, DuplicateEmailException, MethodArgumentNotValidException)
export class GlobalExceptionHandler implements ExceptionFilter {
  private readonly logger = new Logger(GlobalExceptionHandler.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    // 处理自定义重复邮箱异常
    if (exception instanceof DuplicateEmailException) {
      this.logger.error(`Duplicate email error: ${exception.message}, Path: ${request.url}`);
      response.status(HttpStatus.BAD_REQUEST).json({
        statusCode: HttpStatus.BAD_REQUEST,
        message: '该邮箱已被注册',
        path: request.url,
        timestamp: new Date().toISOString()
      });
      return;
    }

    // 处理参数校验异常
    if (exception instanceof MethodArgumentNotValidException) {
      const validationErrors = exception.getValidationErrors();
      this.logger.error(`Validation error: ${JSON.stringify(validationErrors)}, Path: ${request.url}`);
      response.status(HttpStatus.BAD_REQUEST).json({
        statusCode: HttpStatus.BAD_REQUEST,
        message: '参数格式错误',
        details: validationErrors.map(err => ({ field: err.property, message: err.constraints })),
        path: request.url,
        timestamp: new Date().toISOString()
      });
      return;
    }

    // 处理所有HttpException子类
    if (exception instanceof HttpException) {
      const status = exception.getStatus();
      this.logger.error(`HttpException: ${exception.message}, Path: ${request.url}`);
      response.status(status).json({
        statusCode: status,
        message: exception.message,
        path: request.url,
        timestamp: new Date().toISOString()
      });
      return;
    }
  }
}

2. 捕获全局所有异常(包括非HttpException)

如果想覆盖所有未被捕获的错误(比如代码里的TypeError、普通Error),只需要给@Catch()不传任何参数,然后在catch方法里做类型判断即可:

import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { Logger } from '@nestjs/common';
import { Response, Request } from 'express';

@Catch()
export class GlobalExceptionHandler implements ExceptionFilter {
  private readonly logger = new Logger(GlobalExceptionHandler.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    // 优先处理已知的异常类型
    if (exception instanceof HttpException) {
      const status = exception.getStatus();
      this.logger.error(`HttpException: ${exception.message}, Path: ${request.url}`);
      response.status(status).json({
        statusCode: status,
        message: exception.message,
        path: request.url,
        timestamp: new Date().toISOString()
      });
      return;
    }

    // 处理自定义非Http异常
    if ((exception as any).name === 'DuplicateEmailException') {
      this.logger.error(`Duplicate email error: ${(exception as Error).message}, Path: ${request.url}`);
      response.status(HttpStatus.BAD_REQUEST).json({
        statusCode: HttpStatus.BAD_REQUEST,
        message: '该邮箱已被注册',
        path: request.url,
        timestamp: new Date().toISOString()
      });
      return;
    }

    // 兜底处理所有未知错误
    this.logger.error(`Unexpected error: ${(exception as Error).message}\nStack: ${(exception as Error).stack}`);
    response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
      statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
      message: '服务器内部错误',
      path: request.url,
      timestamp: new Date().toISOString()
    });
  }
}

最后:注册全局Filter

不管用哪种方式,都需要在main.ts里把Filter注册为全局生效:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { GlobalExceptionHandler } from './exception-handler.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 注册全局异常过滤器
  app.useGlobalFilters(new GlobalExceptionHandler());
  await app.listen(3000);
}
bootstrap();

这样一来,你的NestJS应用就拥有了和Spring @RestControllerAdvice几乎一致的全局异常处理能力,既能精准处理特定异常,也能兜底所有未知错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:44:06