第十七章 自定义ExceptionFilter

在nestjs中,Exception Filter(异常过滤器)是用于处理全局异常的一种机制。它可以捕获应用程序中发生的异常,并对其进行统一处理。本章我们来学习自定义Exception Filter。

首先 先创建一个新的项目:

nest new exception-filter

1719630765771.png
启动项目:

npm run start:dev

1719634571168.png
在controller 里抛个异常,修改app.controller.ts:

import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) { }

  @Get()
  getHello(): string {
    throw new HttpException('xxxxx', HttpStatus.BAD_REQUEST)
    return this.appService.getHello();
  }
}

HttpStatus 其实就是内置的一些状态码
1719636411087.png
此时 游览器访问 http://localhost:3000/ 可以看到返回的响应是400
1719636511371.png
以上返回的响应格式是内置 Exception Filter 生成
同时 也可以直接抛出具体的异常

import { BadRequestException, Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) { }

  @Get()
  getHello(): string {
    // throw new HttpException('xxxxx', HttpStatus.BAD_REQUEST)
    throw new BadRequestException('异常');
    return this.appService.getHello();
  }
}

继续游览器访问 http://localhost:3000/
1719636798754.png
接着我们定义一个 exception filter:

nest g filter test --flat --no-spec

1719636975288.png
修改 test.filter.ts:

import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';

@Catch(BadRequestException)
export class TestFilter implements ExceptionFilter {
  catch(exception: BadRequestException, host: ArgumentsHost) {
    const http = host.switchToHttp()

    const response = http.getResponse()

    const statusCode = exception.getStatus()

    response.status(statusCode).json({
      code: statusCode,
      message: exception.message,
      error: 'Bad Request',
      xxx: 111
    })
  }
}

在AppModule 里全局引入:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TestFilter } from './test.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalFilters(new TestFilter())

  await app.listen(3000);
}
bootstrap();

访问 http://localhost:3000/ 可以看到响应是自定义的
1719638775133.png
但是目前只是@Catch 了 BadRequestException的异常 如果抛出的是其他异常 那么依然是原来的格式
例如使用BadGatewayException 格式

import { BadGatewayException, BadRequestException, Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) { }

  @Get()
  getHello(): string {
    // throw new HttpException('xxxxx', HttpStatus.BAD_REQUEST)
    throw new BadGatewayException('异常');
    return this.appService.getHello();
  }
}

再访问 http://localhost:3000/ 可以看到依然是默认格式
1719639090326.png
接下来我们看一下BadRequestException 和 BadGatewayException 都是哪里来的
1719639180280.png
1719639260748.png
可以看到 BadRequestException 和 BadGatewayException 都是 HttpException 的子类
那么我们在异常过滤那里将 @Catch 指定 HttpException 即可包含所有

@Catch(HttpException)
export class TestFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const http = host.switchToHttp()

    const response = http.getResponse()

    const statusCode = exception.getStatus()

    response.status(statusCode).json({
      code: statusCode,
      message: exception.message,
      error: 'Bad Request',
      xxx: 111
    })
  }
}

再次访问 http://localhost:3000/ 可以看到现在的格式是自定义的格式
1719639389756.png
自此HttpException 都会被处理,但是当我们用 ValidationPipe 也有问题例如:
创建 www.dto.ts

export class WwwDto {
    aaa: string;
    bbb: number;
}

创建接口

  @Post('www')
  www(@Body() www: WwwDto) {
    return www
  }

安装需要用到的包:

npm install --save class-validator class-transformer

接着修改WwwDto 添加校验规则:

import { IsEmail, IsNotEmpty, IsNumber } from "class-validator";

export class WwwDto {
    @IsNotEmpty({ message: 'aaa 不能为空' })
    @IsEmail({}, { message: 'aaa 不是邮箱格式' })
    aaa: string;

    @IsNumber({}, { message: 'bbb 不是数字' })
    @IsNotEmpty({ message: 'bbb 不能为空' })
    bbb: number;
}

接着全局使用ValidationPipe:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TestFilter } from './test.filter';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalFilters(new ValidationPipe() as any)

  app.useGlobalFilters(new TestFilter())

  await app.listen(3000);
}
bootstrap();

最后在postman 里测试一下:
1719640439889.png
可以看到提示的错误不对, exception filter 会拦截所有 HttpException,但是没有对这种情况做支持。
我们再次把  app.useGlobalFilters(new TestFilter()) 注释掉
访问
1719640659914.png
接着我们修改 test.filter.ts 实现对ValidationPipe的支持

import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class TestFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const http = host.switchToHttp()

    const response = http.getResponse()

    const statusCode = exception.getStatus()

    const res = exception.getResponse() as { message: string[] };

    response.status(statusCode).json({
      code: statusCode,
      message: res?.message?.join ? res?.message?.join(',') : exception.message,
      error: 'Bad Request',
      xxx: 111
    })
  }
}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TestFilter } from './test.filter';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe())

  app.useGlobalFilters(new TestFilter())

  await app.listen(3000);
}
bootstrap();

1719640859043.png
自此ValidationPipe 的错误和其他的错误就都返回了正确的格式

如果想在 Filter 里注入 AppService 则需要修改注册方式:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { TestFilter } from './test.filter';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe())

 // app.useGlobalFilters(new TestFilter())

  await app.listen(3000);
}
bootstrap();

接着在 AppModule 里注册一个 token 为 APP_FILTER 的 provider:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { APP_FILTER } from '@nestjs/core';
import { TestFilter } from './test.filter';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_FILTER,
      useClass: TestFilter
    }
  ],
})
export class AppModule { }

Nest 会把所有 token 为 APP_FILTER 的 provider 注册为全局 Exception Filter
接着就可以在TestFilter 注入 AppService

import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter, HttpException, Inject } from '@nestjs/common';
import { AppService } from './app.service';

@Catch(HttpException)
export class TestFilter implements ExceptionFilter {

  @Inject(AppService)
  private service: AppService
  catch(exception: HttpException, host: ArgumentsHost) {
    const http = host.switchToHttp()

    const response = http.getResponse()

    const statusCode = exception.getStatus()

    const res = exception.getResponse() as { message: string[] };

    response.status(statusCode).json({
      code: statusCode,
      message: res?.message?.join ? res?.message?.join(',') : exception.message,
      error: 'Bad Request',
      xxx: 111,
      www: this.service.getHello()
    })
  }
}

访问 http://localhost:3000/
1719641426835.png

相关推荐

  1. 第二:mybatis plus 如何定义 SQL 查询条件

    2024-07-10 14:34:05       30 阅读
  2. C++ primer

    2024-07-10 14:34:05       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 14:34:05       51 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 14:34:05       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 14:34:05       44 阅读
  4. Python语言-面向对象

    2024-07-10 14:34:05       55 阅读

热门阅读

  1. kubekey在ubuntu24实现kubernetes快速安装

    2024-07-10 14:34:05       20 阅读
  2. C语言快速入门

    2024-07-10 14:34:05       15 阅读
  3. SCI 模块/插槽

    2024-07-10 14:34:05       21 阅读
  4. 模拟防止重复提交

    2024-07-10 14:34:05       19 阅读
  5. OCR的基本概念和开源项目介绍

    2024-07-10 14:34:05       17 阅读
  6. Transformer模型论文解读、源码分析和项目实践

    2024-07-10 14:34:05       17 阅读