NestJS 异常过滤器
基础语法
自定义异常过滤器必须实现 ExceptionFilter 接口(来自 @nestjs/common),该接口仅定义一个 catch 方法:
ts
interface ExceptionFilter<T = any> {
catch(exception: T, host: ArgumentsHost): void;
}参数说明
shell
# 基础参数
exception: 捕获到的异常实例(类型可通过泛型限定)
ArgumentsHost: 执行上下文封装器,核心作用是适配不同运行环境(HTTP / 微服务 / WebSocket),并获取请求、响应等核心对象。
# ArgumentsHost 下的方法
getType() 获取当前环境类型(http/rpc/ws)
switchToHttp() 切换到 HTTP 上下文,返回 HttpArgumentsHost
switchToRpc() 切换到微服务 RPC 上下文,返回 RpcArgumentsHost
switchToWs() 切换到 WebSocket 上下文,返回 WsArgumentsHost
getArgs() 获取原始参数数组(不同环境参数不同,HTTP 下为 [req, res, next])
# ArgumentsHost 下的 switchToHttp() 下的方法
getRequest(): 获取 Express/Fastify 的 req 对象;
getResponse(): 获取 Express/Fastify 的 res 对象;
getNext(): 获取 Express/Fastify 的 next 函数。自定义异常过滤器
以处理 HTTP 异常为例,创建 http-exception.filter.ts
ts
// src/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
// @Catch 装饰器指定要捕获的异常类型(可传多个,如 @Catch(HttpException, Error))
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
// 核心方法:捕获异常后执行的逻辑
catch(exception: HttpException, host: ArgumentsHost) {
// 1. 获取请求上下文(支持不同执行上下文:HTTP/RPC/WebSocket)
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
// 2. 提取异常信息
const status = exception.getStatus(); // HTTP 状态码
const exceptionResponse = exception.getResponse(); // 异常响应体(字符串/对象)
// 3. 标准化响应格式
const errorResponse = {
code: status,
timestamp: new Date().toISOString(),
path: request.url,
message:
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message || '请求失败',
data: null,
};
// 4. 返回自定义响应
response.status(status).json(errorResponse);
// 可选:记录异常日志
console.error(`[${new Date().toLocaleString()}] ${request.method} ${request.url}`, exception);
}
}自定义业务异常
实际开发中,常需要自定义业务异常(如 “用户不存在”“参数校验失败”),结合过滤器处理
- 创建自定义异常类
ts
// src/exceptions/business.exception.ts
import { HttpException, HttpStatus } from '@nestjs/common';
// 业务异常类(继承 HttpException)
export class BusinessException extends HttpException {
// 自定义属性:业务错误码
private readonly businessCode: number;
constructor(
businessCode: number,
message: string,
statusCode: HttpStatus = HttpStatus.BAD_REQUEST
) {
super({ businessCode, message }, statusCode);
this.businessCode = businessCode;
}
getBusinessCode(): number {
return this.businessCode;
}
}- 改造过滤器支持业务异常
ts
// src/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
import { BusinessException } from '../exceptions/business.exception';
// 同时捕获 HttpException 和 BusinessException
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
// 区分业务异常和普通 HTTP 异常
if (exception instanceof BusinessException) {
const businessCode = exception.getBusinessCode();
const exceptionResponse = exception.getResponse() as { message: string };
response.status(status).json({
code: businessCode, // 业务错误码
timestamp: new Date().toISOString(),
path: request.url,
message: exceptionResponse.message,
data: null,
});
} else {
// 普通 HTTP 异常处理逻辑(同之前)
const exceptionResponse = exception.getResponse();
const errorResponse = {
code: status,
timestamp: new Date().toISOString(),
path: request.url,
message:
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message || '请求失败',
data: null,
};
response.status(status).json(errorResponse);
}
}
}- 使用自定义业务异常
ts
// src/app.controller.ts
import { Controller, Get, HttpStatus } from '@nestjs/common';
import { BusinessException } from './exceptions/business.exception';
@Controller()
export class AppController {
@Get('business-error')
throwBusinessError() {
// 抛出业务异常:错误码 1001,提示“用户不存在”
throw new BusinessException(1001, '用户不存在', HttpStatus.NOT_FOUND);
}
}