Nest.js系列——内置 Pipe 和自定义 Pipe

285 阅读5分钟

在 NestJS 框架中,Pipe 是一个强大的功能,用于在请求处理程序执行之前验证和转换数据。就是在参数传递到处理函数之前做一些验证和转换处理的 class,NestJS 内置了一系列常用的 Pipe,并且允许开发者根据需求创建自定义 Pipe。下面我们将介绍各种内置 Pipe 的使用例子,并展示如何实现一个自定义 Pipe。内置的 Pipe 有这些:

  • ValidationPipe
  • ParseIntPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • ParseFilePipe
  • ParseFloatPipe
  • DefaultValuePipe

ValidationPipe

在 NestJS 中,ValidationPipe 用于自动执行输入数据验证。这通常与类类型 (DTO - Data Transfer Objects) 一起使用。一般依赖与两个库来实现class-validatorclass-transformer.

class-validatorclass-transformer 是两个在 TypeScriptJavaScript 项目中经常联合使用的库,它们提供了一套装饰器和一些函数来实现数据的验证和转换功能。在许多服务器端框架中,尤其是在 NestJS 中,这两个库用来处理和验证客户端发送到服务器的数据并进行类型转换。

在项目中一般会全局开启这个管道来对数据进行验证

// 全局管道
app.useGlobalPipes(new ValidationPipe())

ParseIntPipe

在路由中拿参数一般情况下是 string 类型,ParseIntPipe就对拿到的数据进行转换,简单的用法就是直接在控制器方法中添加管道

@Get()
  getHello(@Query('water', ParseIntPipe) water: string): string {
    return water;
  }

通过这个管道,可以将字符串类型的参数转化为数字类型

image.png

如果传递的参数是不能转化为数字类型的话,那么就是会报错了。 image.png 可以看到这里的错误吗是 400,其实这个错误码是可以更改的,改变下管道的调用方式加一些参数。

@Get('water')
  getWater(
    @Query(
      'water',
      new ParseIntPipe({
        errorHttpStatusCode: HttpStatus.NOT_FOUND,
      }),
    )
    water: string,
  ): string {
    return water;
  }

然后调用下这个接口,看下返回结果是什么 image.png 由图中可以看出状态码已经更改为 404 了。当然也可以直接抛出一个错误,让过滤器去拦截,或者使用自定义过滤器处理。

@Get('error')
  getError(
    @Query(
      'water',
      new ParseIntPipe({
        exceptionFactory(error) {
          console.log(error);
          throw new HttpException('xxx ' + error, HttpStatus.NOT_IMPLEMENTED);
        },
      }),
    )
    water: string,
  ): string {
    return water;
  }

在调用下就能看到错误信息已经改变

image.png

ParseFloatPipe

这个管道是转换为浮点数的,可以使用下看看效果

@Get('water-float')
  getFloat(@Query('water', ParseFloatPipe) water: number) {
    return water + 2;
  }

看下访问的结果 image.png 类型为字符串的参数被转换为浮点数并做了计算。

ParseBoolPipe

通过这个管道把参数值转换为布尔值

@Get('water-bool')
  getBool(@Query('water', ParseBoolPipe) water: boolean) {
    return water;
  }

image.png 但是能转换的值有限只接受严格的‘true’、‘false’,其他传入的参数会报错

image.png

ParseArrayPipe

这个管道会把传入的参数按照一定的规则解析成数组类型,但是这个依赖class-validatorclass-transformer这两个包,前面已经安装过了。

@Get('water-arr')
  getArr(
    @Query(
      'water',
      new ParseArrayPipe({
        items: Number,
      }),
    )
    water: Array<number>,
  ) {
    return water;
  }

调用这个接口传入参数看返回的解析后结果

image.png 这里使用的时候可以看到,传入了一个参数为items: Number,其实这个就是把数组的每个元素设置为数字,如果不设置这个参数的话返回的会是字符串数组,来实验一下,

@Get('water-arr')
  getArr(
    @Query(
      'water',
      ParseArrayPipe
    )
    water: Array<number>,
  ) {
    return water;
  }

再次访问下,看返回结果:

image.png 但是这里,还有个问题,如果不传参数就会报错,其实也是有配置可以解决的,配置一下

@Get('water-arr')
  getArr(
    @Query(
      'water',
      new ParseArrayPipe({
        items: Number,
        optional: true,
      }),
    )
    water: Array<number>,
  ) {
    return water;
  }

这样就不会报错了,但是解析数组有时候还会有一种情况,比如前后端约定转换时候不是安装,来进行切割的,比如按照;那就需要有改变,这里也是支持指定的,来配置一下

@Get('water-arr')
getArr(
  @Query(
    'water',
    new ParseArrayPipe({
      items: Number,
      separator: ';',
      optional: true,
    }),
  )
water: Array<number>,
) {
  return water;
}

调用下看返回结果

image.png

ParseUUIDPipe

这个就比较简单了,可以转换判断下参数是不是 uuid

@Get('water-uuid')
  getUuid(@Query('water', ParseUUIDPipe) water: string) {
    return water;
  }

image.png

ParseEnumPipe

这个管道可以限制传递的参数必须是枚举中的值,如果不是就会报错

enum E {
  AAA = '111',
}
@Get('water-enmu')
  getEnmu(@Query('water', new ParseEnumPipe(E)) water: E) {
    return water;
  }

image.png

image.png

DefaultValuePipe

这个管道可以用来设置默认值,如果没有没有传递任何参数那么就用默认值

@Get('water-df')
  getDf(@Query('water', new DefaultValuePipe('water')) water: string) {
    return water;
  }

image.png 如果有参数则使用传递的参数

image.png

自定义 pipe

以上就是这些内置的 pipe,可以满足一些日常使用,但是很显然,有时候有一些特殊的需求需要自定义一些 pipe,这里就简单写一个自定义的 pipe,先了解如何定义一个自定义的 pipe。先使用 cli 自带的命令创建一个自定义的 pipe。

nest g pipe custom-pipe --flat --no-spec

这个命令会在项目根目录下生成一个 pipe 文件

- custom-pipe.pipe.ts

默认生成一个简单的 pipe 代码

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

@Injectable()
export class CustomPipePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    console.log(value, metadata)

    return value
  }
}

这里面加了一行打印日志,看一看接受的内容是什么,然后先写一个接口调用下这个自定义的 pipe。

import { CustomPipePipe } from './custom-pipe.pipe';

@Get('custom-pipe')
  getCustomPipe(@Query('water', CustomPipePipe) water: string) {
    return water;
  }

调用下这个接口,看看返回值

image.png 再来看看自定义的 pipe 中答应的 log 是什么

image.png 由以上可以看出,pipe 中能够获取到请求参数的类型,和获取参数的方式以及参数的值,在这个里面做一些自定义的操作就行了。这就是一个简单的自定义 pipe。

小结

这里简单介绍了 nest 中内置的 pipe 和简单实现了一个自定义的 pipe,这样能够对 pipe 的工作方式有一个自己的理解,希望这个文章对你有帮助,多谢支持。