likes
comments
collection
share

Nest框架中对数据处理和验证 => 管道(Pipe)

作者站长头像
站长
· 阅读数 18

管道与过滤器极为相似,运用在控制器中,不清楚的同学,可以先看看这两块,有助于更好的理解

管道能干什么

管道有两个典型的应用场景:

  • 转换:管道将输入的数据,转换为所需的数据并输出(例如,将字符串转换为整数)
  • 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

在这两种情况下, 管道参数(arguments) 是由 控制器(controllers)的路由处理程序 来进行处理。

Nest 会在调用控制器方法之前插入一个管道,管道会先拦截方法的调用参数,对参数进行转换或是验证处理,然后将转换好或是验证通过的参数,放入方法中进行逻辑处理,如果转换或验证失败,则会抛出异常。

注意:如果在 Pipe 处理和验证中发生异常,controller 不会继续执行任何方法


Nest 自带九个开箱即用的管道:

  1. ValidationPipe:验证管道是一个基于class-validator库的管道,它可以验证输入数据的正确性。它可以验证输入数据的类型、格式、长度、范围等,如果输入数据不符合要求,则会抛出异常。
  2. ParseIntPipe:将输入数据转换为整数类型,如果无法转换,则会抛出异常。
  3. ParseFloatPipe:将输入数据转换为浮点数类型,如果无法转换,则会抛出异常。
  4. ParseBoolPipe:将输入数据转换为布尔类型,如果无法转换,则会抛出异常。
  5. ParseArrayPipe:将输入数据转换为数组类型,可以指定分隔符和数组元素的类型。如果无法转换,则会抛出异常。
  6. ParseUUIDPipe:将输入数据转换为UUID(通用唯一标识符)类型,如果无法转换,则会抛出异常。
  7. ParseEnumPipe:将输入数据转换为枚举类型,如果无法转换,则会抛出异常。
  8. DefaultValuePipe:如果输入数据为undefined,则使用默认值。
  9. ParseFilePipe:将输入数据解析为文件对象,可以指定文件大小和文件类型。如果无法解析,则会抛出异常。

创建管道

使用CIL快速创建管道,执行$ nest g pipe user 命令

$ nest g pipe user

管道的转换应用

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

@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}

转换器(PipeTransform)、注入器(Injectable)、参数元数据(ArgumentMetadata)和错误异常(BadRequestException)

配置管道来处理所传参数 id:

@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
  return this.catsService.findOne(id);
}

是按 ID 从数据库中选择一个现有的用户实体

@Get(':id')
findOne(@Param('id', UserByIdPipe) userEntity: UserEntity) {
  return userEntity;
}

绑定管道

转换 URL 中的数据参数

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

@Param('id', ParseIntPipe)表示该参数会从请求的URL中获取,并使用ParseIntPipe进行转换,将其转换为整数类型。

验证查询字符串参数

@Get()
async findOne(@Query('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

@Query:用于获取请求中的查询参数,即在URL中以“?”后面的参数,例如:http://example.com/api/cats?id=1,其中id就是查询参数

@Param:用于获取请求URL中的路径参数,即在URL中以“/”分隔的参数,例如:http://example.com/api/cats/1,其中1就是路径参数。

管道验证

验证管道,验证通过返回值不变,验证失败抛出异常

使用class-validatorclass-transformer库,创建一个类

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

在控制器中,绑定ValidationPipe并使用

@Post()
async create(
  @Body(new ValidationPipe()) createCatDto: CreateCatDto,
) {
  this.catsService.create(createCatDto);
}

全局管道

全局管道用于整个应用程序、每个控制器和每个路由处理程序

在入口文件中,使用useGlobalPipes()方法,新建一个全局通道

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 全局管道
  app.useGlobalPipes(new ValidationPipe());
  
  await app.listen(3000);
}
bootstrap();

从任何模块外部注册的全局管道(即使用了 useGlobalPipes(), 如上例所示)无法注入依赖,因为它们不属于任何模块

为了能灵活的注入依赖,可以模块中设置通道

// app.module.ts
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe
    }
  ]
})
export class AppModule {}

管道默认值

当接收到 null 或者 undefined 值时,它们会抛出异常。如果想不抛出异常,就可以给他一个默认值

DefaultValuePipe 提供了默认值这种能力

@Get()
async findAll(
  @Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
  @Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
  return this.catsService.findAll({ activeOnly, page });
}
转载自:https://juejin.cn/post/7244721015371694137
评论
请登录