nestJs 管道

发布时间 2023-04-03 18:08:27作者: makalo

文档:https://docs.nestjs.cn/9/pipes

管道是什么

管道是具有 @Injectable() 装饰器的类。管道应实现 PipeTransform 接口。

img

管道的作用

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

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

内置管道

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

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

他们从 @nestjs/common 包中导出

内置管道案例

创建 P 模块

nest g resource p

image-20230403153416720

案例1: string 转 number

我们接受的路由参数希望是一个number 类型 现在是string

image-20230403154312746

这时候可以用 ParseIntPipe内置管道

例:

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
    console.log('id type===========>: ', typeof id);
    return this.pService.findOne(+id);
}

image-20230403155010551

案例2:验证UUId

安装uuid

npm install uuid -S
npm install @types/uuid -D

关键代码

// uuid 用于后面正确的验证
import * as uuid from 'uuid';
console.log(uuid.v4());

// 验证uuid
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
	return this.pService.findOne(+id);
}

完整代码

import { Controller, Get, Post, Body, Patch, Param, Delete, ParseUUIDPipe } from '@nestjs/common';
import { PService } from './p.service';
import { CreatePDto } from './dto/create-p.dto';
import { UpdatePDto } from './dto/update-p.dto';

// uuid 用于后面正确的验证
import * as uuid from 'uuid';
console.log(uuid.v4());

@Controller('p')
export class PController {
  constructor(private readonly pService: PService) {}

  @Post()
  create(@Body() createPDto: CreatePDto) {
    return this.pService.create(createPDto);
  }

  @Get()
  findAll() {
    return this.pService.findAll();
  }

  // 验证uuid
  @Get(':id')
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    return this.pService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updatePDto: UpdatePDto) {
    return this.pService.update(+id, updatePDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.pService.remove(+id);
  }
}

测试

uuid 格式错误

效果如图

image-20230403160115621

uuid 格式正确

上面代码里面我们生成了一个uuid

7f145099-e9c7-47d9-99fb-a0a3f9a39bd7

正确的就通过了

image-20230403160337881

管道验证DTO

其实也叫类验证器,文档:

https://docs.nestjs.cn/9/pipes?id=类验证器

官方内置的功能太少了,这时候可以使用类验证器的方式使用第三方的

安装

npm i --save class-validator class-transformer

文档

https://github.com/typestack/class-validator

验证修饰器

通用验证装饰器

修饰器 描述(英文) 描述(中文)
@IsDefined(value: any) Checks if value is defined (!== undefined, !== null). This is the only decorator that ignores skipMissingProperties option. 检查是否定义了值 (!== undefined, !== null)。这是唯一忽略skipMissingProperties选项的装饰程序
@IsOptional() Checks if given value is empty (=== null, === undefined) and if so, ignores all the validators on the property. 检查给定值是否为空(=== null, === undefined) ,如果是,则忽略属性上的所有验证程序
@Equals(comparison: any) Checks if value equals ("===") comparison. 检查值是否相等(“===”)比较
@NotEquals(comparison: any) Checks if value not equal ("!==") comparison. 检查值是否不相等(“!==”)比较
@IsEmpty() Checks if given value is empty (=== ‘’, === null, === undefined). 检查给定值是否为空(=== ‘’, === null, === undefined)
@IsNotEmpty() Checks if given value is not empty (!== ‘’, !== null, !== undefined). 检查给定值是否不为空 (!== ‘’, !== null, !== undefined)
@IsIn(values: any[]) Checks if value is in a array of allowed values. 检查值是否在允许值的数组中
@IsNotIn(values: any[]) Checks if value is not in a array of disallowed values. 检查值是否不在不允许的值数组中

类型验证装饰器

修饰器 描述(英文) 描述(中文)
@IsBoolean() Checks if a value is a boolean. 是否为布尔值
@IsDate() Checks if the value is a date. 是否为日期
@IsString() Checks if the string is a string. 是否为字符串
@IsNumber(options: IsNumberOptions) Checks if the value is a number. 是否为数字
@IsInt() Checks if the value is an integer number. 是否为整数
@IsArray() Checks if the value is an array 是否为数组
@IsEnum(entity: object) Checks if the value is an valid enum 是否是有效的枚举
Number validation decorators

数字验证装饰器

修饰器 描述(英文) 描述(中文)
@IsDivisibleBy(num: number) Checks if the value is a number that’s divisible by another. 是否是可以被另一个数整除的数
@IsPositive() Checks if the value is a positive number greater than zero. 是否是大于0的整数
@IsNegative() Checks if the value is a negative number smaller than zero. 是否是小于0的整数
@Min(min: number) Checks if the given number is greater than or equal to given number. 是否大于等于给定的数
@Max(max: number) Checks if the given number is less than or equal to given number. 是否小于等于给定的数

日期验证装饰器

修饰器 描述(英文) 描述(中文)
@MinDate(date: Date) Checks if the value is a date that’s after the specified date. 是否在指定日期之后
@MaxDate(date: Date) Checks if the value is a date that’s before the specified date. 是否在指定日期之前

字符串类型验证装饰器

修饰器 描述(英文) 描述(中文)
@IsBooleanString() Checks if a string is a boolean (e.g. is “true” or “false”). 是否为布尔值(例如“true”或“false”)
@IsDateString() Alias for @IsISO8601(). @IsISO8601()的别名
@IsNumberString(options?: IsNumericOptions) Checks if a string is a number. 检查字符串是否为数字

字符串验证装饰器

修饰器 描述(英文) 描述(中文)
@Contains(seed: string) Checks if the string contains the seed. 是否包含种子
@NotContains(seed: string) Checks if the string not contains the seed. 是否不包含种子
@IsAlpha() Checks if the string contains only letters (a-zA-Z). 是否只包含字母
@IsAlphanumeric() Checks if the string contains only letters and numbers. 是否只包含字母和数字
@IsDecimal(options?: IsDecimalOptions) Checks if the string is a valid decimal value. Default IsDecimalOptions are force_decimal=False, decimal_digits: '1,', locale: 'en-US' 是否为有效的十进制值。默认的IsDecimalOptions是force_decimal=False,decimal_digits:‘1’,locale:‘en-US’
@IsAscii() Checks if the string contains ASCII chars only. 是否只包含ASCII字符
@IsBase32() Checks if a string is base32 encoded. 是否是base32编码的
@IsBase64() Checks if a string is base64 encoded. 是否是base64编码的
@IsIBAN() Checks if a string is a IBAN (International Bank Account Number). 是否为IBAN(国际银行帐号)
@IsBIC() Checks if a string is a BIC (Bank Identification Code) or SWIFT code. 是BIC(银行识别码)还是SWIFT码
@IsByteLength(min: number, max?: number) Checks if the string’s length (in bytes) falls in a range. 长度(以字节为单位)是否在某个范围内
@IsCreditCard() Checks if the string is a credit card. 是否为信用卡
@IsCurrency(options?: IsCurrencyOptions) Checks if the string is a valid currency amount. 是否为有效的货币金额
@IsEthereumAddress() Checks if the string is an Ethereum address using basic regex. Does not validate address checksums. 是否是以太坊地址。不验证地址校验和
@IsBtcAddress() Checks if the string is a valid BTC address. 是否为有效的BTC地址
@IsDataURI() Checks if the string is a data uri format. 是否为数据uri格式
@IsEmail(options?: IsEmailOptions) Checks if the string is an email. 是否为电子邮件
@IsFQDN(options?: IsFQDNOptions) Checks if the string is a fully qualified domain name (e.g. domain.com). 是否是完全限定的域名(例如domain.com)
@IsFullWidth() Checks if the string contains any full-width chars. 是否包含任何全角字符
@IsHalfWidth() Checks if the string contains any half-width chars. 是否包含任何半角字符
@IsVariableWidth() Checks if the string contains a mixture of full and half-width chars. 是否包含全半角字符
@IsHexColor() Checks if the string is a hexadecimal color. 是否为十六进制颜色
@IsHSLColor() Checks if the string is an HSL color based on CSS Colors Level 4 specification. 是否是基于CSS Colors Level 4规范的HSL颜色
@IsRgbColor(options?: IsRgbOptions) Checks if the string is a rgb or rgba color. 是rgb还是rgba颜色
@IsIdentityCard(locale?: string) Checks if the string is a valid identity card code. 是否是有效的身份证代码(估计是国外的身份证格式)
@IsPassportNumber(countryCode?: string) Checks if the string is a valid passport number relative to a specific country code. 是否是相对于特定国家代码的有效护照号码
@IsPostalCode(locale?: string) Checks if the string is a postal code. 是否是邮政编码
@IsHexadecimal() Checks if the string is a hexadecimal number. 是否为十六进制数
@IsOctal() Checks if the string is a octal number. 是否为八进制数
@IsMACAddress(options?: IsMACAddressOptions) Checks if the string is a MAC Address. 是否为MAC地址
@IsIP(version?: "4"|"6") Checks if the string is an IP (version 4 or 6). 是否为IP(版本4或6)
@IsPort() Checks if the string is a valid port number. 是否为有效的端口号
@IsISBN(version?: "10"|"13") Checks if the string is an ISBN (version 10 or 13). 是否为ISBN(版本10或13)
@IsEAN() Checks if the string is an if the string is an EAN (European Article Number). 是否为EAN(欧洲商品编号)
@IsISIN() Checks if the string is an ISIN (stock/security identifier). 是否为ISIN(股票/安全标识符)
@IsISO8601(options?: IsISO8601Options) Checks if the string is a valid ISO 8601 date format. Use the option strict = true for additional checks for a valid date. 是否为有效的ISO 8601日期格式。对于有效日期的其他检查,请使用选项strict=true。
@IsJSON() Checks if the string is valid JSON. 是否为有效的JSON
@IsJWT() Checks if the string is valid JWT. 是否为有效的JWT
@IsObject() Checks if the object is valid Object (null, functions, arrays will return false). 是否为有效对象(null、函数、数组将返回false)
@IsNotEmptyObject() Checks if the object is not empty. 对象是否为空
@IsLowercase() Checks if the string is lowercase. 是否为小写
@IsLatLong() Checks if the string is a valid latitude-longitude coordinate in the format lat, long. 是否为lat,long格式的有效经纬度坐标
@IsLatitude() Checks if the string or number is a valid latitude coordinate. 检查字符串或数字是否是有效的纬度坐标
@IsLongitude() Checks if the string or number is a valid longitude coordinate. 检查字符串或数字是否为有效的经度坐标
@IsMobilePhone(locale: string) Checks if the string is a mobile phone number. 是否是移动电话号码
@IsISO31661Alpha2() Checks if the string is a valid ISO 3166-1 alpha-2 officially assigned country code. 是否是有效的iso3166-1alpha-2官方指定的国家代码。
@IsISO31661Alpha3() Checks if the string is a valid ISO 3166-1 alpha-3 officially assigned country code. 是否是有效的iso3166-1alpha-3官方指定的国家代码。
@IsLocale() Checks if the string is a locale. 是否为区域设置
@IsPhoneNumber(region: string) Checks if the string is a valid phone numberusing libphonenumber-js. 是否是有效的电话号码
@IsMongoId() Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. 是否是MongoDB ObjectId的有效十六进制编码表示形式
@IsMultibyte() Checks if the string contains one or more multibyte chars. 是否包含一个或多个多字节字符
@IsNumberString(options?: IsNumericOptions) Checks if the string is numeric. 是否包含任何代理项对字符
@IsSurrogatePair() Checks if the string contains any surrogate pairs chars. 是否包含任何代理项对字符
@IsUrl(options?: IsURLOptions) Checks if the string is an url. 是否为url
@IsMagnetURI() Checks if the string is a magnet uri format. 是否为magneturi格式
@IsUUID(version?: "3"|"4"|"5"|"all") Checks if the string is a UUID (version 3, 4, 5 or all ). 是否是UUID(version 3、4、5或all)
@IsFirebasePushId() Checks if the string is a Firebase Push ID 是否为Firebase Push ID
@IsUppercase() Checks if the string is uppercase. 是否为大写
@Length(min: number, max?: number) Checks if the string’s length falls in a range. 符串的长度是否在某个范围内
@MinLength(min: number) Checks if the string’s length is not less than given number. 字符串的长度是否不小于给定的数字
@MaxLength(max: number) Checks if the string’s length is not more than given number. 字符串的长度是否不大于给定的数字
@Matches(pattern: RegExp, modifiers?: string) Checks if string matches the pattern. Either matches(‘foo’, /foo/i) or matches(‘foo’, ‘foo’, ‘i’). 是否与模式匹配,匹配(‘foo’,/foo/i)或匹配(‘foo’,‘foo’,‘i’)
@IsMilitaryTime() Checks if the string is a valid representation of military time in the format HH:MM. 是否是HH:MM格式的有效军事时间表示形式
@IsHash(algorithm: string) Checks if the string is a hash The following types are supported:md4, md5, sha1, sha256, sha384, sha512, ripemd128, ripemd160, tiger128, tiger160, tiger192, crc32, crc32b. 是否是散列,以下类型是supported:md4、md5、sha1、sha256、sha384、sha512、ripemd128、ripemd160、tiger128、tiger128、tiger192、crc32、crc32b。
@IsMimeType() Checks if the string matches to a valid MIME type format 是否与有效的MIME类型格式匹配
@IsSemVer() Checks if the string is a Semantic Versioning Specification (SemVer). 是否为语义版本控制规范(SemVer)
@IsISSN(options?: IsISSNOptions) Checks if the string is a ISSN. 是否为ISSN
@IsISRC() Checks if the string is a ISRC. 是否为ISRC
@IsRFC3339() Checks if the string is a valid RFC 3339 date. 是否为有效的RFC 3339日期

数组验证装饰器

修饰器 描述(英文) 描述(中文)
@ArrayContains(values: any[]) Checks if array contains all values from the given array of values. 是否包含给定值数组中的所有值
@ArrayNotContains(values: any[]) Checks if array does not contain any of the given values. 是否不包含任何给定值
@ArrayNotEmpty() Checks if given array is not empty. 是否为空
@ArrayMinSize(min: number) Checks if the array’s length is greater than or equal to the specified number. 数组的长度是否大于或等于指定的数字
@ArrayMaxSize(max: number) Checks if the array’s length is less or equal to the specified number. 数组的长度是否小于或等于指定的数字
@ArrayUnique(identifier?: (o) => any) Checks if all array’s values are unique. Comparison for objects is reference-based. Optional function can be speciefied which return value will be used for the comparsion. 所有数组的值是否唯一。对象的比较是基于引用的。可选函数可以指定用于比较的返回值

对象验证装饰器

修饰器 描述(英文) 描述(中文)
@IsInstance(value: any) Checks if the property is an instance of the passed value. 属性是否是传递值的实例

其他验证装饰器

修饰器 描述(英文) 描述(中文)
@Allow() Prevent stripping off the property when no other constraint is specified for it. 防止在没有为属性指定其他约束时剥离该属性

管道验证DTO 案例

创建一个管道

nest g pi 文件名字

例:

nest g pi p

image-20230403162618682

创建DTO 验证类

例:

import {IsNotEmpty,IsString} from 'class-validator'

export class CreatePDto {
    @IsNotEmpty()//验证是否为空
    @IsString() //是否为字符串
    name:string;
 
    @IsNotEmpty()
    age:number
}

image-20230403163617338

在controller 使用管道 和 定义类型

关键代码

// 类验证器
@Post()
create(@Body(PPipe) createPDto: CreatePDto) {
	return this.pService.create(createPDto);
}

完整代码

import { Controller, Get, Post, Body, Patch, Param, Delete, ParseUUIDPipe } from '@nestjs/common';
import { PService } from './p.service';
import { CreatePDto } from './dto/create-p.dto';
import { UpdatePDto } from './dto/update-p.dto';
import { PPipe } from './p/p.pipe'

// uuid 用于后面正确的验证
import * as uuid from 'uuid';
console.log(uuid.v4());

@Controller('p')
export class PController {
  constructor(private readonly pService: PService) {}

  // 类验证器
  @Post()
  create(@Body(PPipe) createPDto: CreatePDto) {
    return this.pService.create(createPDto);
  }

  @Get()
  findAll() {
    return this.pService.findAll();
  }

  // 验证uuid
  @Get(':id')
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    return this.pService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updatePDto: UpdatePDto) {
    return this.pService.update(+id, updatePDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.pService.remove(+id);
  }
}

image-20230403170555645

实现验证transform

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

@Injectable()
export class PPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    // value 就是 前端传过来的数据
    // metaData 就是元数据 通过 metatype 可以去实例化这个类
    console.log(value, metadata);
    // 实例化DTO
    const DTO = plainToInstance(metadata.metatype, value);
    // 通过 validate 验证 DTO 返回一个promise 的错误信息
    const errors = await validate(DTO);
    // 如果有错误抛出
    if(errors.length > 0){
      throw new HttpException(errors, HttpStatus.BAD_REQUEST);
    }
    return value;
  }
}

image-20230403170959458

测试

格式错误

image-20230403170728968

格式正确

image-20230403170815722

注册全局的DTO验证管道

https://docs.nestjs.cn/9/pipes?id=全局管道

上面那个案例是我们根据官方文档写的,其实官方已经提供了一个写好的

只不过我们使用自己的可以自定义,你也可以全局注册自己的验证管道

注册

全局的一般在main.ts中注册

例:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

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

image-20230403172132052

使用

上面那个例子,我们使用的是自定义的,这次使用全局的

全局是默认的,所以不用特意写

image-20230403172311067

测试

格式错误

image-20230403172417501

格式正确

image-20230403172528359