NestJS异常处理 #
异常处理概述 #
NestJS内置了异常处理层,负责捕获应用中未处理的异常,并返回适当的响应给用户。
内置异常 #
HttpException #
typescript
import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
}
响应:
json
{
"statusCode": 403,
"message": "Forbidden"
}
常用内置异常 #
| 异常类 | 状态码 | 说明 |
|---|---|---|
BadRequestException |
400 | 错误请求 |
UnauthorizedException |
401 | 未授权 |
NotFoundException |
404 | 未找到 |
ForbiddenException |
403 | 禁止访问 |
NotAcceptableException |
406 | 不可接受 |
RequestTimeoutException |
408 | 请求超时 |
ConflictException |
409 | 冲突 |
GoneException |
410 | 已不存在 |
PayloadTooLargeException |
413 | 负载过大 |
UnsupportedMediaTypeException |
415 | 不支持的媒体类型 |
UnprocessableEntityException |
422 | 无法处理的实体 |
InternalServerErrorException |
500 | 内部服务器错误 |
NotImplementedException |
501 | 未实现 |
BadGatewayException |
502 | 错误网关 |
ServiceUnavailableException |
503 | 服务不可用 |
使用内置异常 #
typescript
import {
Controller,
Get,
NotFoundException,
UnauthorizedException,
BadRequestException,
} from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string) {
if (!id) {
throw new BadRequestException('ID is required');
}
const user = this.usersService.findOne(id);
if (!user) {
throw new NotFoundException(`User ${id} not found`);
}
return user;
}
}
自定义响应 #
typescript
@Get()
findAll() {
throw new HttpException(
{
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
details: { reason: 'Access denied' },
},
HttpStatus.FORBIDDEN,
);
}
响应:
json
{
"status": 403,
"error": "This is a custom message",
"details": {
"reason": "Access denied"
}
}
自定义异常 #
创建自定义异常 #
typescript
import { HttpException, HttpStatus } from '@nestjs/common';
export class UserNotFoundException extends HttpException {
constructor(id: string) {
super(`User with ID ${id} not found`, HttpStatus.NOT_FOUND);
}
}
export class UserAlreadyExistsException extends HttpException {
constructor(email: string) {
super(
{
status: HttpStatus.CONFLICT,
error: 'User already exists',
email,
},
HttpStatus.CONFLICT,
);
}
}
使用自定义异常 #
typescript
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string) {
const user = this.usersService.findOne(id);
if (!user) {
throw new UserNotFoundException(id);
}
return user;
}
}
异常过滤器 #
创建异常过滤器 #
typescript
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@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();
const exceptionResponse = exception.getResponse();
response.status(status).json({
code: status,
timestamp: new Date().toISOString(),
path: request.url,
message:
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message,
});
}
}
注册过滤器 #
方法级别 #
typescript
@Post()
@UseFilters(new HttpExceptionFilter())
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
控制器级别 #
typescript
@Controller('users')
@UseFilters(new HttpExceptionFilter())
export class UsersController {}
全局级别 #
typescript
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
捕获所有异常 #
typescript
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException
? exception.getResponse()
: 'Internal server error';
response.status(status).json({
code: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
}
}
捕获特定异常 #
typescript
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
import { QueryFailedError } from 'typeorm';
@Catch(QueryFailedError)
export class DatabaseExceptionFilter implements ExceptionFilter {
catch(exception: QueryFailedError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
response.status(500).json({
code: 500,
message: 'Database error',
detail: exception.message,
});
}
}
继承BaseExceptionFilter #
typescript
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// 自定义逻辑
console.error('Exception caught:', exception);
// 调用父类方法
super.catch(exception, host);
}
}
异常处理最佳实践 #
1. 统一异常响应格式 #
typescript
export class BusinessException extends HttpException {
constructor(
message: string,
private readonly errorCode: string,
status: HttpStatus = HttpStatus.BAD_REQUEST,
) {
super({ message, errorCode }, status);
}
getErrorCode(): string {
return this.errorCode;
}
}
@Catch(BusinessException)
export class BusinessExceptionFilter implements ExceptionFilter {
catch(exception: BusinessException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
response.status(exception.getStatus()).json({
success: false,
code: exception.getStatus(),
errorCode: exception.getErrorCode(),
message: exception.message,
timestamp: new Date().toISOString(),
});
}
}
2. 错误码定义 #
typescript
export enum ErrorCode {
USER_NOT_FOUND = 'USER_NOT_FOUND',
USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS',
INVALID_PASSWORD = 'INVALID_PASSWORD',
UNAUTHORIZED = 'UNAUTHORIZED',
FORBIDDEN = 'FORBIDDEN',
}
export class UserNotFoundException extends BusinessException {
constructor(id: string) {
super(
`User with ID ${id} not found`,
ErrorCode.USER_NOT_FOUND,
HttpStatus.NOT_FOUND,
);
}
}
3. 服务层异常 #
typescript
@Injectable()
export class UsersService {
async findOne(id: string): Promise<User> {
const user = await this.usersRepository.findOne({ where: { id } });
if (!user) {
throw new UserNotFoundException(id);
}
return user;
}
}
4. 验证异常处理 #
typescript
import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';
import { ValidationError } from 'class-validator';
@Catch(ValidationError)
export class ValidationExceptionFilter implements ExceptionFilter {
catch(exception: ValidationError[], host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const errors = exception.map(error => ({
property: error.property,
constraints: error.constraints,
}));
response.status(HttpStatus.BAD_REQUEST).json({
code: HttpStatus.BAD_REQUEST,
message: 'Validation failed',
errors,
});
}
}
异常日志 #
typescript
import { ExceptionFilter, Catch, ArgumentsHost, Logger } from '@nestjs/common';
@Catch()
export class LoggingExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(LoggingExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const request = ctx.getRequest();
const response = ctx.getResponse();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
this.logger.error(
`Exception caught: ${request.method} ${request.url}`,
exception instanceof Error ? exception.stack : undefined,
);
response.status(status).json({
code: status,
message: 'An error occurred',
timestamp: new Date().toISOString(),
});
}
}
总结 #
本章学习了NestJS异常处理:
- 内置HTTP异常
- 自定义异常
- 异常过滤器的创建和使用
- 全局异常处理
- 异常处理最佳实践
接下来,让我们学习 中间件。
最后更新:2026-03-28