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
}
}