7. 异常过滤器:Nest.js 的异常处理工具

6 阅读4分钟

7. 异常过滤器:Nest.js 的异常处理工具

介绍

欢迎回来!在前几篇文章中,我们已经了解了如何创建控制器、服务、中间件和管道,并使用它们来处理 HTTP 请求。在这篇文章中,我们将探讨 Nest.js 中的异常过滤器(Exception Filters)。异常过滤器是处理应用中异常情况的强大工具,可以捕获和处理异常,并返回自定义的响应。让我们一起深入了解异常过滤器的工作原理和使用方法。

什么是异常过滤器?

异常过滤器是一个类,它实现了 ExceptionFilter 接口,并包含一个 catch 方法。catch 方法接收两个参数:异常对象和当前的请求上下文。异常过滤器可以用于捕获和处理应用中的异常,并返回自定义的响应。

创建一个异常过滤器

让我们通过一个实际例子来了解如何创建和使用异常过滤器。假设我们要创建一个全局异常过滤器,用于捕获所有未处理的异常,并返回统一的错误响应。

首先,使用 Nest CLI 创建一个新的异常过滤器:

nest generate filter all-exceptions

这条命令会在 src 目录下生成一个 all-exceptions.filter.ts 文件。让我们看看这个文件的内容:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception instanceof HttpException ? exception.getStatus() : 500;

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}
  • @Catch():将类标记为异常过滤器。
  • catch 方法:异常过滤器的核心方法,接收异常对象和当前的请求上下文。

使用异常过滤器

要使用异常过滤器,我们需要将其应用到特定的控制器或全局范围内。让我们将 AllExceptionsFilter 注册为全局异常过滤器。

打开 main.ts 文件,修改如下:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './all-exceptions.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new AllExceptionsFilter());
  await app.listen(3000);
}
bootstrap();

在这个例子中,我们使用 app.useGlobalFilters 方法将 AllExceptionsFilter 注册为全局异常过滤器。这样,所有未处理的异常都会被 AllExceptionsFilter 捕获并处理。

自定义异常响应

让我们修改 AllExceptionsFilter,返回更详细的错误响应,包括错误信息和堆栈跟踪(仅在开发环境中)。

修改 all-exceptions.filter.ts 文件如下:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;

    const errorResponse = {
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception instanceof HttpException ? exception.getResponse() : 'Internal server error',
    };

    if (process.env.NODE_ENV !== 'production') {
      errorResponse['stack'] = exception instanceof Error ? exception.stack : null;
    }

    response.status(status).json(errorResponse);
  }
}

在这个例子中,我们返回了更详细的错误响应,包括错误信息和堆栈跟踪(仅在开发环境中)。

捕获特定异常

有时,我们可能只想捕获特定类型的异常。我们可以使用 @Catch 装饰器指定要捕获的异常类型。

让我们创建一个新的异常过滤器,用于捕获 HttpException 类型的异常。

首先,使用 Nest CLI 创建一个新的异常过滤器:

nest generate filter http-exception

然后,修改 http-exception.filter.ts 文件如下:

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();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception.getResponse(),
    });
  }
}

在这个例子中,我们使用 @Catch(HttpException) 装饰器指定要捕获的异常类型为 HttpException

要使用这个异常过滤器,我们可以将其应用到特定的控制器或全局范围内。让我们将 HttpExceptionFilter 应用到 BooksController

打开 books.controller.ts 文件,修改如下:

import { Controller, Get, Post, Put, Delete, Param, Body, UseFilters } from '@nestjs/common';
import { BooksService } from './books.service';
import { CreateBookDto } from './create-book.dto';
import { HttpExceptionFilter } from './http-exception.filter';

@Controller('books')
@UseFilters(HttpExceptionFilter)
export class BooksController {
  constructor(private readonly booksService: BooksService) {}

  @Get()
  findAll(): string {
    return this.booksService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return this.booksService.findOne(id);
  }

  @Post()
  create(@Body() createBookDto: CreateBookDto): string {
    return this.booksService.create(createBookDto);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateBookDto: CreateBookDto): string {
    return this.booksService.update(id, updateBookDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string): string {
    return this.booksService.remove(id);
  }
}

在这个例子中,我们使用 @UseFilters 装饰器将 HttpExceptionFilter 应用到 BooksController。每次 BooksController 中的方法抛出 HttpException 时,都会被 HttpExceptionFilter 捕获并处理。

结论

在这篇文章中,我们深入探讨了 Nest.js 中的异常过滤器,并通过实际例子展示了如何创建和使用异常过滤器。我们还学习了如何将异常过滤器应用到特定的控制器或全局范围内,以及如何捕获和处理特定类型的异常。

异常过滤器是处理应用中异常情况的强大工具,可以捕获和处理异常,并返回自定义的响应。通过使用异常过滤器,我们可以确保应用在异常情况下的行为更加一致和可控。

感谢你的阅读!如果你有任何问题或建议,欢迎在评论区留言。我们下次再见!

预告

在下一篇文章中,我们将探讨 Nest.js 中的守卫(Guards)。守卫是处理请求授权的强大工具,可以在请求到达控制器之前检查用户的权限。敬请期待!