第六章 AOP架构 Middleware、Guard、Pipe、Interceptor、ExceptionFilter

Nest AOP 是指在 Nest.js 框架中使用面向切面编程(Aspect-Oriented Programming)的技术。面向切面编程是一种编程范式,它允许开发者通过在代码中定义切面(Aspect),来将横切关注点(Cross-cutting Concerns)与核心业务逻辑进行解耦。

在 Nest.js 中,开发者可以使用 Nest AOP 模块来实现 AOP 功能。通过使用装饰器(Decorator)来定义切面,并使用注入依赖(Dependency Injection)来实现对切面的使用。Nest AOP 提供了一系列的装饰器和注解,开发者可以使用它们来在不修改原有代码的情况下,为应用程序的不同部分添加额外的行为。
使用 Nest AOP,开发者可以很方便地实现一些跟业务逻辑无关但又必要的功能,比如日志记录、性能监控、缓存等。同时,它也能提高代码的可维护性和可测试性,使应用程序更易于理解和扩展。
当请求过来,可能会经过 Controller(控制器)、Service(服务)、Repository(数据库访问) 的逻辑

当我们想在这些逻辑里加入一些通用逻辑该怎么加呢?
可能我们会直接想到在哪用到就在哪里增加代码,但是这样会造成通用的逻辑入侵到了业务代码里面,所以AOP (面向切面编程) 孕育而生,它的作用是一些通用逻辑分离到切面中,保持业务逻辑的纯粹性,这样切面逻辑可以复用,还可以动态的增删。
例如下图:

Nest 实现 AOP 的方式更多,一共有五种,包括 Middleware、Guard、Pipe、Interceptor、ExceptionFilter。

1、创建项目

首先我们先创建一个项目

nest new aop

1716821071392.png

2、中间件 Middleware

Nest中间件(Middleware)是一种用于处理HTTP请求和响应的中间层。它可以在处理请求之前和之后执行一些操作,例如验证请求、记录日志、处理异常等。

在Nest中,中间件是一个类或函数,它实现了NestMiddleware接口。在类中,可以使用use方法装饰器将该中间件注册到应用程序模块中。在函数中,可以使用@Middleware()装饰器将该中间件注册到相应的路由处理器上。
中间件可以被全局应用或只应用于特定的路由或控制器。全局中间件会应用于应用程序中所有的请求。而特定路由或控制器的中间件只会应用于指定的路由或控制器。

2.1、全局使用

在 main.ts 里通过 app.use 使用:

  app.use(function (req: Request, res: Response, next: NextFunction) {
    console.log('之前', req.url);
    next();
    console.log('之后');
  })

1716906053616.png
在 app.controller.ts 中添加

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

1716906148040.png
接着启动项目

pnpm run start:dev

1716821876454.png
浏览器访问 http://localhost:3000/
1716822017496.png
可以看到控制台按照顺序打印出了 之前 进入 之后

这里其实和vue的router路由守卫差不多意思 在进入之前 可以进行一些逻辑判断 接着调用next() 就可进入 接着执行完成里面的内容之后 可以再进行一些操作

1716906176346.png
大家有兴趣可以多建几个接口测试一下 例如

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

1716906561170.png
接着我们游览器打开 http://localhost:3000/www 可以看到控制台
1716906633330.png
这就是全局中间件 Middleware 它可以让我们在调用接口之前 做一些逻辑复用 在处理请求之前和之后执行一些操作

2.1、路由使用

中间件 Middleware除了可以在全局使用 也支持路由中间件

首先 创建一个路由中间件:

nest g middleware log --no-spec --flat

–no-spec 是不生成测试文件 --flat 是平铺,不生成目录
1716907766874.png
可以看到生成以下代码
1716907809302.png
接着我们修改一下 log.middleware.ts

import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LogMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('之前', req.url);
    next();
    console.log('之后');
  }
}

并把之前的全局中间件 Middleware 注释掉
1717220250382.png
我们在 app.module.ts 的 AppModule 中 implements NestModule{,在 configure 方法里配置
consumer.apply(LogMiddleware).forRoutes(‘aaa*’);
此处是配置哪些路由生效

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LogMiddleware } from './log.middleware';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LogMiddleware).forRoutes('www*')
  }
}

接着我们游览器打开 http://localhost:3000/www 可以看到控制台
1717220610552.png
1717220627695.png这就是全局中间件和路由中间件的区别。

3、路由守卫 Guard

NestJS 提供了一种称为 Guard 的路由守卫机制。Guard 可以用于保护路由,只有满足特定条件的请求才能继续执行路由处理程序。 可以用于在调用某个 Controller 之前判断权限,返回 true 或者 false 来决定是否放行

3.1、接口路由守卫

首先我们创建个 Guard:

nest g guard login --no-spec --flat

1717224596828.png
生成如下代码:
1717224647722.png
上面代码实现了CanActivate接口,用于判断用户是否具有激活路由的权限。canActivate方法接收一个ExecutionContext上下文对象作为参数,返回一个布尔值、Promise或Observable,用于表示是否可以激活对应的路由。
我们在代码这加入打印语句 接着把return 返回 false 代表只要调用了这个路由守卫 的接口 都是失败的

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class LoginGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('登录 校验');
    return false;
  }
}

接着我们在部分接口 使用 路由守卫 如下代码:(app.controller.ts)

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';

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

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

  /**
   * 使用了路由守卫的接口
   */
  @Get('test')
  @UseGuards(LoginGuard)
  test(): string {
    return '调用成功'
  }
}

接着我们打开 http://localhost:3000/test 调用test 接口
1717225692270.png
可以看到 没有调用test接口的权限 返回了403
其他接口是可以正常调用的 只有这个test接口设置了路由守卫 返回了false代表 用了路由守卫的接口都没有权限访问
1717225751615.png

3.2、全局路由守卫

首先我们把之前 app.controller.ts 的 @UseGuards(LoginGuard) 删除掉先
接着我们在 main.ts 中引入 await app.useGlobalGuards(new LoginGuard())

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NextFunction } from 'express';
import { LoginGuard } from './login.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // app.use(function (req: Request, res: Response, next: NextFunction) {
  //   console.log('之前', req.url);
  //   next();
  //   console.log('之后');
  // })
  await app.useGlobalGuards(new LoginGuard())
  await app.listen(3000);

}
bootstrap();

接着访问 http://localhost:3000/www http://localhost:3000/test 可以看到接口都403 没有权限访问 代表全局路由守卫使用成功
1717226680198.png1717226696717.png

除了在main.ts中使用 await app.useGlobalGuards(new LoginGuard())的全局方式 还有另一种方式

我们先把第一种全局方式的使用先注释掉 如下图:
1717226980376.png
在 AppModule 里声明:
1717227258638.png

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LogMiddleware } from './log.middleware';
import { APP_GUARD } from '@nestjs/core';
import { LoginGuard } from './login.guard';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: LoginGuard
    }
  ],
})

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LogMiddleware).forRoutes('www*')
  }
}

接着访问 http://localhost:3000/www http://localhost:3000/test 可以看到接口都403 没有权限访问 代表全局路由守卫使用成功
1717227305300.png
1717227318243.png
可以看到,全局路由守卫 Guard 依然是生效的

两种全局守卫的区别:
1、在main.ts 手动 new 的 Guard 实例: 不在 IoC 容器里
2、provider 的方式:在 IoC 容器里的,可以注入别的 provider

4、拦截器 Interceptor

在 NestJS 中,拦截器 (Interceptor) 是一种特殊的中间件,可以在请求处理流程中的各个阶段进行拦截和操作。拦截器可以用来实现一些通用的功能,比如在请求处理之前或之后进行日志记录、权限验证、数据转换等操作。它可以对请求和响应进行修改,也可以抛出异常来中断请求处理流程。

首先我们创建个 interceptor:

nest g interceptor time --no-spec --flat

1717228129547.png
1717228185717.png
该代码段是一个Nest.js框架中的拦截器(Interceptor)实现,用于拦截HTTP请求并记录处理时间。具体来说:

  • TimeInterceptor类实现了NestInterceptor接口,表明它是一个拦截器。
  • intercept方法是拦截器的核心方法,会在请求处理之前被调用。
  • intercept方法接收两个参数:context提供了关于当前请求和路由的信息,next是CallHandler接口的实例,用于继续处理请求链。
  • 方法内部通过调用next.handle()来继续处理请求链,并返回处理结果的Observable。

Controller 之前之后的处理逻辑可能是异步的。Nest 里通过 rxjs 来组织它们,所以可以使用 rxjs 的各种 operator。
time.interceptor.ts 内容更改如下:

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

/**
 * TimeInterceptor 类实现了NestInterceptor接口,用于拦截请求并进行时间记录。
 */
@Injectable()
export class TimeInterceptor implements NestInterceptor {
  /**
   * intercept 方法会在请求处理之前被调用。
   * @param context 执行上下文,提供了关于当前请求和路由的信息。
   * @param next CallHandler 接口的实例,用于继续处理请求链。
   * @returns 返回一个Observable,代表处理请求的结果。
   */
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {

    const startTime = Date.now();

    // 继续处理请求链,并返回结果
    return next.handle().pipe(
      tap(() => {
        console.log('time: ', Date.now() - startTime)
      })
    );
  }
}

先把之前的路由守卫注释掉:
1717228677306.png

接着在接口上使用拦截器 如下:(app.controller.ts)

import { Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';

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

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

  /**
   * 使用了路由守卫的接口
   */
  @Get('test')
  // @UseGuards(LoginGuard)

  @UseInterceptors(TimeInterceptor)
  test(): string {
    return '调用成功'
  }
}

1717228832046.png
调用 http://localhost:3000/test 接口
1717228957048.png
1717228988991.png
可以看到,interceptor 生效了

Interceptor 和 Middleware 的区别,参数的不同,interceptor 可以拿到调用的 controller 和 handler,如下 打印 context.getClass(), context.getHandler():

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

/**
 * TimeInterceptor 类实现了NestInterceptor接口,用于拦截请求并进行时间记录。
 */
@Injectable()
export class TimeInterceptor implements NestInterceptor {
  /**
   * intercept 方法会在请求处理之前被调用。
   * @param context 执行上下文,提供了关于当前请求和路由的信息。
   * @param next CallHandler 接口的实例,用于继续处理请求链。
   * @returns 返回一个Observable,代表处理请求的结果。
   */
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {

    console.log(context.getClass(), context.getHandler());


    const startTime = Date.now();

    // 继续处理请求链,并返回结果
    return next.handle().pipe(
      tap(() => {
        console.log('time: ', Date.now() - startTime)
      })
    );
  }
}

再次调用 http://localhost:3000/test 接口
1717229259354.png
Interceptor 支持每个路由单独启用,只作用于某个 handler,也可以在 controller 级别启动,作用于下面的全部 handler

import { Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';

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

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

  /**
   * 使用了路由守卫的接口
   */
  @Get('test')
  // @UseGuards(LoginGuard)

  test(): string {
    return '调用成功'
  }
}

1717229397196.png
这样 这个 controller 下的所有接口都会经过拦截器

同时也支持两种 全局的方式

1、第一种

  app.useGlobalInterceptors(new TimeInterceptor())

1717229590792.png
2、第二种

 {
      provide: APP_INTERCEPTOR,
      useClass: TimeInterceptor
}

1717229718552.png
除了路由的权限控制、目标 Controller 之前之后的处理这些都是通用逻辑外,对参数的处理也是一个通用的逻辑,所以 Nest 也抽出了对应的切面,也就是 Pipe

5、管道 Pipe

nestjs中的Pipe是一种中间件,它用于在处理请求之前或之后对请求进行过滤、验证或转换。Pipe可以用来检查输入数据的有效性、转换数据格式、应用默认值等。

在nestjs中,可以使用自定义Pipe来处理请求的输入数据。自定义Pipe必须实现PipeTransform接口,并实现transform方法。transform方法接收两个参数:value和metadata。其中,value是请求输入的数据,metadata是包含有关装饰器元数据的对象。
nestjs还提供了一些内置的Pipe,用于处理常见的验证和转换场景。例如,ValidationPipe用于验证输入数据是否符合指定的验证规则,并返回具体的验证错误信息。ParseIntPipe用于将输入数据转换为整数类型。
在nestjs中,可以在控制器中使用Pipe装饰器来应用Pipe。例如,可以使用@UsePipes装饰器将一个或多个Pipe应用于整个控制器或特定的方法。可以使用@Pipe装饰器将单个Pipe应用于控制器或方法。

首先创建个 pipe:

nest g pipe validate --no-spec --flat

1717230044052.png
1717230104624.png
以实现 transform 方法 为例:
里面可以对传入的参数值 value 做参数验证,比如格式、类型是否正确,不正确就抛出异常。也可以做转换,返回转换后的值。 这里的 value 就是传入的参数,如果不能转成数字,就返回参数错误,否则乘 2 再传入

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class ValidatePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {

    if(Number.isNaN(parseInt(value))){
      throw new BadRequestException(`参数${metadata.data}错误`)
    }

    return typeof value === 'number' ? value * 0.1 : parseInt(value) * 2 ;
  }
}

1717230515818.png

5.1、接口级别Pipe

接着在 app.controller.ts 新增一个接口

  @Get('ccc')
  ccc(@Query('num', ValidatePipe) num: number) {
    return num + 1;
  }

1717241630091.png
http://localhost:3000/ccc?num=5
1717230670002.png
http://localhost:3000/ccc?num=%E5%BC%82%E5%B8%B8
1717230714586.png
可以看到,参数错误的时候返回了 400 响应,参数正确的时候也乘 10 传入了 handler。
这就是 Pipe 的作用。

5.2、controller级别Pipe

import { Controller, Get, Query, UseGuards, UseInterceptors, UsePipes } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';

@Controller()
// @UseInterceptors(TimeInterceptor)
@UsePipes(ValidatePipe)
export class AppController {
  constructor(private readonly appService: AppService) { }

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

  @Get('test')
  // @UseGuards(LoginGuard)
  // @UseInterceptors(TimeInterceptor)
  test(): string {
    return '调用成功'
  }

  @Get('ccc')
  ccc(@Query('num') num: number) {
    return num + 1;
  }
}

http://localhost:3000/ccc?num=5
1717230670002.png
http://localhost:3000/ccc?num=%E5%BC%82%E5%B8%B8
1717230714586.png
可看到如果定义在controller级别 则整个controller中的接口都有此校验

5.2、全局级别Pipe

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NextFunction } from 'express';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // app.use(function (req: Request, res: Response, next: NextFunction) {
  //   console.log('之前', req.url);
  //   next();
  //   console.log('之后');
  // })
  //  app.useGlobalGuards(new LoginGuard())

  app.useGlobalPipes(new ValidatePipe())

  await app.listen(3000);

}
bootstrap();

1717242156106.png
Nest 内置了一些 Pipe 如下

  1. ValidationPipe 是一个预定义的管道,用于验证请求体、请求参数、请求头等数据的有效性

ValidationPipe可以在控制器路由处理程序执行之前,通过使用装饰器将其应用在相应的控制器方法上。它会自动根据装饰器提供的元数据将输入数据校验为特定的类。

  1. ParseIntPipe 用于将路径参数或查询参数中的字符串转换为整数类型。

当定义一个路由处理程序(controller)并指定路径参数或查询参数时,可以使用ParseIntPipe来确保接收到的参数是整数类型。

  1. ParseBoolPipe 用于将传入的字符串参数解析为布尔值

ParseBoolPipe的作用是将传入的字符串参数解析为布尔值。如果传入的字符串参数是"true"或"t"或"1",则解析为true;如果传入的字符串参数是"false"或"f"或"0",则解析为false。

  1. ParseArrayPipe 用于将输入数据解析为数组。它可以用于验证和转换输入数据。

使用ParseArrayPipe可以方便地将输入数据转换为数组,无论输入数据是字符串、JSON字符串还是其他格式,都可以通过ParseArrayPipe进行解析。

  1. ParseUUIDPipe 是nestjs中的一种预定义管道,主要用于将传入的UUID字符串转换为UUID对象,并进行验证。

当使用 ParseUUIDPipe 时,nestjs会自动将传入的UUID字符串转换为UUID对象,并进行验证。如果传入的字符串不是合法的UUID格式,则会抛出一个异常。这样,我们就可以在控制器中使用 UUID 对象而不是字符串来处理数据。

  1. DefaultValuePipe 用于在控制器方法参数中提供默认值。

使用DefaultValuePipe非常简单,只需在控制器方法参数上加上@Query()装饰器,并在装饰器中传递一个对象参数,对象中包含一个名为’defaultValue’的属性,该属性的值即为默认值。
以下是一个使用DefaultValuePipe的示例:


@Get()
async getUsers(@Query('page', new DefaultValuePipe(1)) page: number) {
  // 如果没有传递page参数,它的值将会是默认值1
  // ...
}

  1. ParseEnumPipe 用于将请求参数转换为枚举类型。

当我们接收到一个请求参数,并希望将其转换为枚举类型时,可以使用ParseEnumPipe。它将自动尝试将请求参数值转换为指定的枚举类型,如果无法转换,则抛出一个错误。

  1. ParseFloatPipe 用于将输入的字符串转换为浮点数类型。它会尝试解析输入字符串,并返回对应的浮点数值。

该管道的作用是在处理输入数据之前,对输入进行验证和转换。通过使用ParseFloatPipe,开发者可以确保输入的字符串可以正确解析为浮点数,并在解析失败时抛出错误。

  1. ParseFilePipe 用于解析文件上传请求中的文件数据。

使用ParseFilePipe时,可以在控制器的参数上使用@ParseFilePipe装饰器,指定要解析的文件字段。例如:


@Post('upload')
uploadFile(@UploadedFile(@ParseFilePipe('file')) file: any) {
  // 处理上传的文件
}

5.3、异常处理 ExceptionFilter

ExceptionFilter 是一个可用于全局异常处理的模块,它允许开发人员自定义处理应用程序中发生的所有异常的行为。通过实现自定义的 ExceptionFilter,可以捕获应用程序中未处理的异常,并针对不同的异常类型执行特定的操作,如记录日志、返回错误信息等。它可以用于处理应用程序抛出的任何类型的异常,包括 HTTP 请求异常和自定义异常。可以将 ExceptionFilter 注册为全局过滤器,或者在特定的控制器或路由上使用局部过滤器。

创建一个 filter

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

1717242628612.png
1717242662349.png
修改代码如下:(test.filter.ts)

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

@Catch(BadRequestException)
export class TestFilter implements ExceptionFilter {
  catch(exception: BadRequestException, host: ArgumentsHost) {

    const response: Response = host.switchToHttp().getResponse();

    response.status(400).json({
      statusCode: 400,
      message: 'test: ' + exception.message
    })
  }
}

以及:(app.controller.ts)

import { Controller, Get, Query, UseFilters, UseGuards, UseInterceptors, UsePipes } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

@Controller()
// @UseInterceptors(TimeInterceptor)
// @UsePipes(ValidatePipe)
export class AppController {
  constructor(private readonly appService: AppService) { }

  @Get()
  getHello(): string {
    console.log('进入');

    return this.appService.getHello();
  }

  @Get('www')
  www(): string {
    console.log('进入 www');
    return '我调用了www'
  }

  @Get('test')
  // @UseGuards(LoginGuard)
  // @UseInterceptors(TimeInterceptor)
  test(): string {
    return '调用成功'
  }

  @Get('ccc')
  @UseFilters(TestFilter)
  ccc(@Query('num') num: number) {
    return num + 1;
  }
}

http://localhost:3000/ccc?num=asdad
1717243093732.png

以下是Nestjs中内置的所有HTTP相关异常:

  1. BadRequestException:表示客户端发出了一个无效的请求。
  2. UnauthorizedException:表示未经授权的请求。
  3. NotFoundException:表示请求的资源不存在。
  4. ForbiddenException:表示禁止访问请求的资源。
  5. NotAcceptableException:表示请求的内容不被服务器接受。
  6. RequestTimeoutException:表示请求超时。
  7. ConflictException:表示请求的资源与现有资源冲突。
  8. GoneException:表示请求的资源已经不存在。
  9. PayloadTooLargeException:表示请求的负载过大。
  10. UnsupportedMediaTypeException:表示请求的媒体类型不受支持。
  11. UnprocessableEntityException:表示请求的实体无法被处理。
  12. InternalServerErrorException:表示服务器内部发生了错误。
  13. NotImplementedException:表示请求的功能尚未实现。
  14. BadGatewayException:表示网关或代理服务器接收到无效的响应。
  15. GatewayTimeoutException:表示网关或代理服务器在请求处理时超时。
  16. ServiceUnavailableException:表示服务器暂时不可用。
  17. GatewayTimeoutException:表示网关或代理服务器在请求处理时超时。

这些是一些常见的HTTP异常,但您也可以根据您的需求自定义自己的异常。

6、AOP 机制的顺序

Middleware、Guard、Pipe、Interceptor、ExceptionFilter 都可以透明的添加某种处理逻辑到某个路由或者全部路由,这就是 AOP 的好处。

在 NestJS 应用中,Middleware、Guard、Pipe、Interceptor 和 ExceptionFilter 的执行顺序如下:

  1. Middleware:NestJS 中的中间件按照注册的顺序依次执行。每个中间件可以在请求被处理之前或之后执行一些操作。在请求到达控制器之前,中间件按照注册的顺序依次执行。在请求处理完成后,中间件按照相反的顺序依次执行。
  2. Guard:守卫层在中间件执行之后,控制器处理之前运行。它可以根据一些条件决定是否允许请求通过。守卫可以是同步的也可以是异步的,可以影响请求的生命周期。
  3. Pipe:管道用于处理请求数据的转换和验证。管道可以在请求到达控制器之前或之后应用。在控制器处理请求之前,NestJS 会将请求数据传递给所有注册的管道并进行处理。
  4. Interceptor:拦截器在控制器处理请求之前和之后运行。拦截器可以用于对请求和响应做一些处理,比如在处理前添加、修改或删除一些数据,或在处理后进行日志记录。
  5. ExceptionFilter:异常过滤器用于处理控制器中出现的异常。它可以捕获并处理控制器中抛出的异常,然后返回适当的响应。

总结起来,NestJS 应用中这些组件的执行顺序是:Middleware -> Guard -> Pipe -> Interceptor -> Controller -> Interceptor -> Pipe -> ExceptionFilter。

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫ゞ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值