Nest.js - 管道

概念

管道是具有@Injectable装饰器的
管道应实现PineTransform接口

管道可以是参数级,方法级,控制器级,全局级

管道原理

管道有两种类型:

  • 转换:管道将输入数据转换成所需的数据输出
  • 验证:对输入数据进行验证,成功则继续传递,失败则抛出异常

Nest会在Controller的路由处理程序之前插入一个管道,Nest会拦截该方法的调用参数,进行转换/验证处理,然后再调用原方法。

内置管道

@nestjs/common自带五个开箱即用的管道:ValidationPipeParseIntPipeParseBoolPipeParseArrayPipeParseUUIDPipe.

某个场景
//create-cat.dto
export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}
//cats.controller.ts
@Post()
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

为了确保create()的正确执行,必须验证CreateCatDto的三个属性。此时可考虑验证管道

  • 控制器路由处理程序中验证(不好);
  • 创建验证器类并委托任务(不好);
  • 验证中间件(不好);

验证管道:第一种验证方式

创建自定义的验证管道

Joi库是一个基于结构的验证库。

//安装依赖
$ npm install --save @hapi/joi
$ npm install --save-dev @types/hapi__joi
//自定义一个验证管道

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

@Injectable()
export class JoiValidationPipe implements PipeTransform {
  constructor(private schema: ObjectSchema) {}

  transform(value: any, metadata: ArgumentMetadata) {
    const { error } = this.schema.validate(value);
    if (error) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }
}
绑定管道(应用管道)
@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
  • 管道可以绑在 controller 或是其方法
  • @UsePipes() 装饰器并创建一个管道实例,并将其传递给 Joi 验证

验证管道:第二种验证方式

class-validator允许基于装饰器的验证,可以通过metatype做很多事.

$ npm i --save class-validator class-transformer
创建相关DTO
//向CreateCatDto类添加一些装饰器

import { IsString, IsInt } from 'class-validator';

export class CreateCatDto {
  @IsString()
  name: string;

  @IsInt()
  age: number;

  @IsString()
  breed: string;
}
创建自定义的验证管道
//创建ValidationPipe 类

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

@Injectable()
export class ValidationPipe implements PipeTransform {
  //1. transform()是异步的,因为有些class-validator的验证是Promise(异步的)
  //2. metatype采用解构赋值从ArgumentMetadata中提取参数
  async transform(value: any, { metatype }: ArgumentMetadata) {
    //当验证类型不是js的数据类型时,跳过验证
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    //因为请求body中不包含类型信息,若class-validator需要使用相关的DTO,需要做类型转换
    const object = plainToClass(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);
  }
}
应用管道(参数级别)
//cats.controller.ts
@Post()
async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
应用管道(方法级别)
//cats.controller.ts
@Post()
@UsePipes(new ValidationPipe()) //ValidationPipe 的实例已就地立即创建
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
//cats.controller.ts
@Post()
@UsePipes(ValidationPipe) //直接传入类(而非实例),让框架承担实例化责任,并启用依赖注入
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

应用管道(全局级别)

//main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe()); //用于整个应用程序、每个控制器和每个路由处理程序
  await app.listen(3000);
}
bootstrap();

转换管道

  • transform 函数返回的值完全覆盖了参数先前的值
定义转换管道
// parse-int.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ParseIntPipe implements PipeTransform {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}
应用管道
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
  return await this.catsService.findOne(id);
}

//@Param('id', new ParseIntPipe()) id 的用法?
  • ParseIntpipe 将在请求触发相应的处理程序之前执行。

内置验证管道

  • ValidationPipeParseIntPipe 是内置管道,不必自己构建这些管道
  • ValidationPipe 需要同时安装 class-validatorclass-transformer

你可能感兴趣的:(Nest.js - 管道)