NestJS如何全局统一捕获处理所有类型的异常
NestJS 全局统一异常处理实现方案
NestJS 原生提供全局异常过滤器扩展点,实现自定义全局异常过滤器即可统一接管所有未被业务层捕获的异常,包括HTTP标准异常、数据库驱动异常、业务自定义异常,最终返回格式统一的响应。
核心实现步骤
- 编写全局异常过滤器,按异常类型分支处理:优先识别NestJS内置HTTP异常,再单独识别处理数据库类异常,剩余未知异常统一按服务端错误处理
- 全局注册过滤器,让逻辑对整个应用的所有路由生效
- 按运行环境控制敏感信息返回,生产环境屏蔽堆栈、SQL语句等敏感内容
1. 编写全局异常过滤器
新建src/filters/all-exceptions.filter.ts文件,代码如下:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger } from '@nestjs/common'; import { Request, Response } from 'express'; // 装饰器参数传空代表捕获所有抛出的异常 @Catch() export class AllExceptionsFilter implements ExceptionFilter { private readonly logger = new Logger(AllExceptionsFilter.name); catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); // 初始化默认响应值 let status = HttpStatus.INTERNAL_SERVER_ERROR; let message = '服务器内部错误'; let bizCode = status; // 处理NestJS内置HTTP异常(含参数校验、404、403等框架自带异常) if (exception instanceof HttpException) { status = exception.getStatus(); const exceptionRes = exception.getResponse(); message = typeof exceptionRes === 'string' ? exceptionRes : (Array.isArray((exceptionRes as any).message) ? (exceptionRes as any).message.join(',') : (exceptionRes as any).message) || message; bizCode = status; } // 处理MySQL等数据库驱动抛出的原生错误 else if (this.isMysqlError(exception)) { const err = exception as any; switch (err.code) { // 唯一键重复错误,即你遇到的ER_DUP_ENTRY(1062) case 'ER_DUP_ENTRY': status = HttpStatus.BAD_REQUEST; bizCode = 40001; // 解析错误信息提取重复字段信息 const dupMatch = err.sqlMessage?.match(/Duplicate entry '(.+?)' for key '(.+?)'/); message = dupMatch ? `提交失败:值「${dupMatch[1]}」已存在,违反唯一约束${dupMatch[2]}` : '提交的数据存在重复记录'; break; // 外键约束失败 case 'ER_ROW_IS_REFERENCED_2': case 'ER_NO_REFERENCED_ROW_2': status = HttpStatus.BAD_REQUEST; bizCode = 40002; message = '操作失败:数据存在关联依赖,无法执行当前操作'; break; // 字段内容超长 case 'ER_DATA_TOO_LONG': status = HttpStatus.BAD_REQUEST; bizCode = 40003; message = '提交失败:部分字段内容长度超出限制'; break; // 其他数据库错误统一归类 default: status = HttpStatus.BAD_REQUEST; bizCode = 40000; message = '数据操作失败'; } // 数据库错误详情只打日志,不返回给前端 this.logger.error(`DB Error: ${err.sqlMessage}, SQL: ${err.sql}`, err.stack); } // 其他未知异常 else { this.logger.error('Unhandled Exception', exception instanceof Error ? exception.stack : exception); } // 统一返回结构 response.status(status).json({ code: bizCode, message, path: request.url, timestamp: new Date().toISOString(), // 仅开发环境返回堆栈信息方便调试 ...(process.env.NODE_ENV === 'development' && { stack: exception instanceof Error ? exception.stack : undefined }) }); } // 判断是否为MySQL驱动抛出的错误 private isMysqlError(err: unknown): boolean { return err instanceof Error && 'code' in err && 'sqlMessage' in err && 'sql' in err; } }
2. 注册全局过滤器
两种注册方式二选一即可:
- 在入口文件
main.ts中直接注册,不需要依赖注入支持:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { AllExceptionsFilter } from './filters/all-exceptions.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 注册全局异常过滤器 app.useGlobalFilters(new AllExceptionsFilter()); await app.listen(3000); } bootstrap();
- 若需要过滤器支持依赖注入(比如在过滤器里注入其他服务),在
AppModule中通过APP_FILTER令牌注册:
import { Module } from '@nestjs/common'; import { APP_FILTER } from '@nestjs/core'; import { AllExceptionsFilter } from './filters/all-exceptions.filter'; @Module({ providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter, }, ], }) export class AppModule {}
扩展说明
- 如果你使用TypeORM、Prisma等ORM,ORM会包装自身的错误类型,只需要在过滤器中新增对应错误类型的判断分支即可,处理逻辑和上述MySQL原生错误一致。
- 如果使用PostgreSQL、MongoDB等其他数据库,只需要调整错误判断逻辑、错误码匹配规则即可复用整套全局处理逻辑。
- 业务自定义异常可以通过继承
HttpException实现,过滤器会自动识别处理,也可以自定义异常类后在过滤器中新增分支做特殊处理。
内容的提问来源于stack exchange,提问作者Muhammad Ahsan Saifi
相关产品推荐
相关产品推荐

