https://nestjs.com/
https://www.jianshu.com/p/eec0586409da
中文文档
https://docs.nestjs.cn

管道pipes

管道范围

  1. 参数范围
  1. //cats.controler.ts
  2. @Post()
  3. async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto) {
  4. this.catsService.create(createCatDto);
  5. }


2. 方法范围

  1. //cats.controler.ts
  2. @Post()
  3. @UsePipes(new ValidationPipe())
  4. async create(@Body() createCatDto: CreateCatDto) {
  5. this.catsService.create(createCatDto);
  6. }
  7. //@UsePipes() 修饰器是从 @nestjs/common 包中导入的
  1. 控制器范围
  2. 全局范围
  1. //main.ts
  2. async function bootstrap() {
  3. const app = await NestFactory.create(ApplicationModule);
  4. app.useGlobalPipes(new ValidationPipe());
  5. await app.listen(3000);
  6. }
  7. bootstrap();

useGlobalPipes() 方法不会为网关和微服务设置管道(正在使用混合应用程序功能)

全局管道用于整个应用程序、每个控制器和每个路由处理程序。就依赖注入而言,从任何模块外部注册的全局管道(如上例所示)无法注入依赖,因为它们不属于任何模块。为了解决这个问题,可以使用以下构造直接为任何模块设置管道

  1. //app.module.ts
  2. import { Module } from '@nestjs/common';
  3. import { APP_PIPE } from '@nestjs/core';
  4. @Module({
  5. providers: [
  6. {
  7. provide: APP_PIPE,
  8. useClass: CustomGlobalPipe,
  9. },
  10. ],
  11. })
  12. export class ApplicationModule {}
  13. //上述6.0官方的示例代码目前是错误的,我们使用了5.0的示例代码。

验证的分类

  1. 路由处理程序(打破单个责任原则(SRP))
  2. 验证器类(每次在方法开始的时候我们都必须使用这个验证器)
  1. //cats.controler.ts
  2. //依赖注入
  3. @Post()
  4. @UsePipes(ValidationPipe)
  5. async create(@Body() createCatDto: CreateCatDto) {
  6. this.catsService.create(createCatDto);
  7. }
  1. 验证中间件(但不可能创建一个通用的中间件,可以在整个应用程序中使用)
  2. 管道验证(最佳)

    验证管道

    对象结构验证

    常用方法之一是使用基于结构的验证。Joi库是一个工具,它允许您使用一个可读的API以非常简单的方式创建结构。为了创建一个使用对象结构的管道,我们需要创建一个简单的类,该类将结构作为 constructor 参数
  1. import * as Joi from 'joi';
  2. import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
  3. @Injectable()
  4. export class JoiValidationPipe implements PipeTransform {
  5. constructor(private readonly schema) {}
  6. transform(value: any, metadata: ArgumentMetadata) {
  7. const { error } = Joi.validate(value, this.schema);
  8. if (error) {
  9. throw new BadRequestException('Validation failed');
  10. }
  11. return value;
  12. }
  13. }

管道绑定非常简单-我们需要使用 @UsePipes() 修饰器并使用有效的Joi结构创建管道实例

  1. @Post()
  2. @UsePipes(new JoiValidationPipe(createCatSchema))
  3. async create(@Body() createCatDto: CreateCatDto) {
  4. this.catsService.create(createCatDto);
  5. }

类验证器

Nest 与 class-validator 配合得很好。这个优秀的库允许您使用基于装饰器的验证。基于装饰器的验证对于管道功能非常强大,因为我们可以访问已处理属性的 metatype。在我们开始之前,我们需要安装所需的软件包

  1. npm i --save class-validator class-transformer
  1. //create-cat.dto.ts
  2. import { IsString, IsInt } from 'class-validator';
  3. export class CreateCatDto {
  4. @IsString()
  5. readonly name: string;
  6. @IsInt()
  7. readonly age: number;
  8. @IsString()
  9. readonly breed: string;
  10. }
  1. //validation.pipe.ts
  2. import { PipeTransform, Pipe, ArgumentMetadata, BadRequestException } from '@nestjs/common';
  3. import { validate } from 'class-validator';
  4. import { plainToClass } from 'class-transformer';
  5. @Injectable()
  6. export class ValidationPipe implements PipeTransform<any> {
  7. async transform(value, metadata: ArgumentMetadata) {
  8. const { metatype } = metadata;
  9. if (!metatype || !this.toValidate(metatype)) {
  10. return value;
  11. }
  12. const object = plainToClass(metatype, value);
  13. const errors = await validate(object);
  14. if (errors.length > 0) {
  15. throw new BadRequestException('Validation failed');
  16. }
  17. return value;
  18. }
  19. private toValidate(metatype): boolean {
  20. const types = [String, Boolean, Number, Array, Object];
  21. return !types.find((type) => metatype === type);
  22. }
  23. }

我们来看看这个代码。首先,请注意 transform() 函数是 异步 的。这是可能的,因为Nest支持同步和异步管道。另外,还有一个辅助函数 toValidate()。由于性能原因,它负责从验证过程中排除原生 JavaScript类型。最后一个重要的是我们必须返回相同的价值。这个管道是一个特定于验证的管道,所以我们需要返回完全相同的属性以避免重写(如前所述,管道将输入转换为所需的输出)。
最后一步是设置 ValidationPipe 。管道,与异常过滤器相同,它们可以是方法范围的、控制器范围的和全局范围的。另外,管道可以是参数范围的。我们可以直接将管道实例绑定到路由参数装饰器,例如@Body()。让我们来看看下面的例子

  1. //cats.controler.ts
  2. @Post()
  3. async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto) {
  4. this.catsService.create(createCatDto);
  5. }

当验证逻辑仅涉及一个指定的参数时,参数范围的管道非常有用。要在方法级别设置管道,您需要使用 UsePipes() 装饰器

  1. //cats.controler.ts
  2. @Post()
  3. @UsePipes(new ValidationPipe())
  4. async create(@Body() createCatDto: CreateCatDto) {
  5. this.catsService.create(createCatDto);
  6. }

转换管道

有时从客户端传来的数据需要经过一些修改。此外,有些部分可能会丢失,所以我们必须应用默认值。转换管道填补了客户端请求和请求处理程序之间的空白

  1. //parse-int.pipe.ts
  2. import { PipeTransform, Pipe, ArgumentMetadata, HttpStatus, BadRequestException } from '@nestjs/common';
  3. @Injectable()
  4. export class ParseIntPipe implements PipeTransform<string> {
  5. async transform(value: string, metadata: ArgumentMetadata) {
  6. const val = parseInt(value, 10);
  7. if (isNaN(val)) {
  8. throw new BadRequestException('Validation failed');
  9. }
  10. return val;
  11. }
  12. }

这是一个 ParseIntPipe,它负责将一个字符串解析为一个整数值。现在我们将管道绑定到选定的参数:

由于上述结构,ParseIntpipe 将在请求触发相应的处理程序之前执行。
另一个有用的例子是按 ID 从数据库中选择一个现有的用户实体

  1. @Get(':id')
  2. findOne(@Param('id', UserByIdPipe) userEntity: UserEntity) {
  3. return userEntity;
  4. }

内置管道

  1. skipMissingProperties boolean 如果设置为 true,则验证程序将跳过验证对象中缺少的属性的验证。
  2. whitelist boolean 如果设置为 true,则验证程序将除去未使用任何装饰器的属性的已验证对象。
  3. forbidNonWhitelisted boolean 如果设置为 true,则验证程序将引发异常,而不是取消非白名单属性。
  4. forbidUnknownValues boolean 如果设置为 true,未知对象的验证将立即失败。
  5. disableErrorMessages boolean 如果设置为 true,验证错误将不会转发到客户端。
  6. groups string[] 验证对象期间要使用的组。
  7. dismissDefaultMessages boolean 如果设置为 true,验证将不使用默认消息。如果错误消息未显式设置,则为 undefined 的。
  8. validationError.target boolean 目标是否应在 ValidationError 中展示
  9. validationError.value boolean 验证值是否应在 ValidationError 中展示。

ValidationPipe 需要同时安装 class-validatorclass-transformer

守卫guards

守卫是一个使用 @Injectable() 装饰器的类。 守卫应该实现 CanActivate 接口

守卫有一个单独的责任。它们确定请求是否应该由路由处理程序处理。到目前为止,访问限制逻辑大多在中间件内。这样很好,因为诸如 token 验证或将 request 对象附加属性与特定路由没有强关联。
但中间件是非常笨的。它不知道调用 next() 函数后会执行哪个处理程序。另一方面,守卫可以访问 ExecutionContext 对象,所以我们确切知道将要执行什么。
守卫在每个中间件之后执行的,但在拦截器和管道之前。

守卫范围

  1. 控制器范围
  2. 方法范围
  3. 全局范围

    授权守卫最好的守卫用例之一就是授权逻辑,因为只有当调用者具有足够的权限时才能使用特定的路由。我们计划创建的 AuthGuard 将按顺序提取并验证请求标头中发送的 token。

    角色守卫

    反射器

    拦截器interceptors

    拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。

拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:

  • 在函数执行之前/之后绑定额外的逻辑
  • 转换从函数返回的结果
  • 转换从函数抛出的异常
  • 扩展基本函数行为
  • 根据所选条件完全重写函数 (例如, 缓存目的)

拦截器的作用与控制器controllers,提供者providers,守卫guards等相同,这意味着它们可以通过构造函数注入依赖项。

拦截器范围

  • 控制器范围
  • 方法范围
  • 全局范围

    响应映射Response mapping

    映射异常Exception mapping

    流重写(缓存拦截器)

    rxjs处理超时使用RxJS运算符操作流的可能性为我们提供了许多功能