Skip to content

NestJS 自定义异常过滤器使用场景

系统化实现异常过滤器步骤

一、自定义业务异常类

自定义业务异常类(如 BusinessException),用于区分系统异常和业务异常

ts
// src/common/exceptions/business.exception.ts

import { HttpException, HttpStatus } from '@nestjs/common';

/**
 * 自定义业务异常类
 * @param message 异常提示信息
 * @param status HTTP 状态码(默认 400)
 * @param errorCode 自定义业务错误码(便于前端区分)
 */
export class BusinessException extends HttpException {
  readonly errorCode: number;

  constructor(
    message: string,
    errorCode: number = 10001, // 自定义业务错误码
    status: HttpStatus = HttpStatus.BAD_REQUEST
  ) {
    super(message, status);
    this.errorCode = errorCode;
  }
}

二、实现自定义异常过滤器

自定义异常过滤器,统一格式化异常响应格式

针对特定异常(如 HttpException、自定义业务异常)做差异化处理

ts
// src/common/filters/global-exception.filter.ts

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { BusinessException } from '../exceptions/business.exception';

// Catch 装饰器指定要捕获的异常(支持多个,此处捕获所有异常 + 特定异常)
@Catch(HttpException, Error)
export class GlobalExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(GlobalExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    // 获取请求上下文
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    // 1. 区分异常类型,提取核心信息
    let status: HttpStatus;
    let message: string;
    let errorCode: number;

    if (exception instanceof BusinessException) {
      // 处理自定义业务异常
      status = exception.getStatus();
      message = exception.message;
      errorCode = exception.errorCode;
    } else if (exception instanceof HttpException) {
      // 处理 Nest 内置 HttpException
      status = exception.getStatus();
      // 兼容 HttpException 的响应体(可能是对象或字符串)
      const exceptionResponse = exception.getResponse();
      message =
        typeof exceptionResponse === 'object'
          ? (exceptionResponse as { message: string }).message
          : (exceptionResponse as string);
      errorCode = 99999; // 系统内置异常默认错误码
    } else {
      // 处理未知异常(如代码运行时错误)
      status = HttpStatus.INTERNAL_SERVER_ERROR;
      message = '服务器内部错误,请联系管理员';
      errorCode = 50000;
      // 记录未知异常日志(便于排查)
      this.logger.error(`未知异常:${(exception as Error).stack}`, (exception as Error).stack);
    }

    // 2. 统一响应格式
    const responseBody = {
      success: false,
      data: null,
      error: {
        code: errorCode,
        message: message,
        path: request.url, // 请求路径
        timestamp: new Date().toISOString(), // 时间戳
      },
    };

    // 3. 设置响应头并返回
    response.status(status).json(responseBody);
  }
}

三、全局注册异常过滤器

全局注册异常过滤器,让所有接口生效

ts
// src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 注册全局异常过滤器
  app.useGlobalFilters(new GlobalExceptionFilter());

  await app.listen(3000);
  console.log(`应用运行在: http://localhost:3000`);
}
bootstrap();

四、业务模块演示

局部注册(针对特定控制器 / 方法)

ts
// src/app.controller.ts(演示不同异常场景)

import { Controller, Get, Param, HttpException, HttpStatus } from '@nestjs/common';
import { BusinessException } from './common/exceptions/business.exception';

@Controller('demo')
export class AppController {
  // 场景1:触发自定义业务异常
  @Get('business/:id')
  testBusinessException(@Param('id') id: string) {
    if (id === '0') {
      // 抛出业务异常(错误码 + 提示 + 状态码)
      throw new BusinessException('ID 不能为 0', 10002, HttpStatus.FORBIDDEN);
    }
    return { success: true, data: { id } };
  }

  // 场景2:触发 Nest 内置 HttpException
  @Get('http/:name')
  testHttpException(@Param('name') name: string) {
    if (name === 'admin') {
      throw new HttpException(
        { message: '禁止访问管理员资源', detail: '权限不足' },
        HttpStatus.UNAUTHORIZED
      );
    }
    return { success: true, data: { name } };
  }

  // 场景3:触发未知运行时异常
  @Get('unknown')
  testUnknownException() {
    // 模拟代码错误(如调用不存在的方法)
    const obj: any = null;
    obj.undefinedMethod(); // 会抛出 TypeError
  }
}