Skip to content

管道

管道是一个使用@Injectable()装饰器注解的类,它实现了PipeTransform接口

管道它要么不变地返回值,要么抛出一个异常

管道有两种典型的用例

  • 转换:将输入数据转换为所需的形式(例如:从字符串到整数)

  • 验证:评估输入数据,如果有效则简单的将其原样传递,否则抛出异常

在这两种情况下,管道都作用于控制器路由处理器正在处理的参数上.

Nest 在方法被调用之前插入管道

管道接收传递给方法的参数并对其进行操作.

任何转换或者验证操作都在此时发生,然后路由处理器使用任何(可能)已转换的参数被调用

Nest 自带了许多内置的管道,你可以直接使用

你也可以构建自己的定制管道

内建管道

  • ValidationPipe:验证输入数据,确保输入数据符合预期格式

  • ParseIntPipe:将输入数据转换为整数

  • ParseBoolPipe:将输入数据转换为布尔值

  • ParseFloatPipe:将输入数据转换为浮点数

  • ParseUUIDPipe:将输入数据转换为 UUID

  • ParseArrayPipe:将输入数据转换为数组

  • ParseEnumPipe:将输入数据转换为枚举

  • DefaultValuePipe:为输入数据设置默认值

  • ParseFilePipe:将输入数据转换为文件

  • 新建一个文件夹起名 pipe ,新建一个文件起名 validation.pipe.ts

ts
import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  HttpException,
} from "@nestjs/common";

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    console.log("value", value);
    console.log(metadata);
    if (value.length != 11) {
      throw new HttpException("手机号格式不正确", 401);
    }
    return value;
  }
}
  • 使用
ts
import { ValidationPipe } from '../pipe/validate.pipe';

  @Post(':id')
  async getGuandao2(
    @Param('id') id: number,
    @Body('phone', ValidationPipe) phone: number,
  ): Promise<string> {
    console.log('见来了啊哈哈哈');
    return 'guandao';
  }

通用验证器

安装

bash

pnpm i class-validator class-transformer -S

写 dto(验证规则)

新建一个文件夹起名 dto,新建一个文件起名 user-create.dto.ts

bash

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

export class UserCreateDto {
  @IsString({ message: '用户名必须是字符串' })
  username: string;
  @IsString({ message: '密码必须是字符串' })
  password: string;
  @IsString({ message: '邮箱必须是字符串' })
  email: string;
  @IsInt({ message: '年龄必须是整数' })
  age: number;
  message: string;
}

创建一个管道

  • (1)抛出异常版本

新建一个文件夹起名 pipe,新建一个文件起名 user-create.pipe.ts

ts
import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  HttpException,
} from "@nestjs/common";

import { plainToInstance } from "class-transformer";

import { validate } from "class-validator";
@Injectable()
export class ValidationPipe2 implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    const { metatype } = metadata;
    console.log(metatype);
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    // 变成对象,把规则和值 组合成验证的对象
    const object = plainToInstance(metatype, value);
    // 验证 errors 是个数组
    const errors = await validate(object);
    if (errors.length > 0) {
      console.log(errors);
      // 第一种只返回第一个
      if (errors.length == 1) {
        for (let arr in errors[0].constraints) {
          throw new HttpException(`${errors[0].constraints[arr]}`, 400);
        }
      }

      // 第二种返回所有
      let result = [];
      errors.forEach((item) => {
        for (let arr in item.constraints) {
          result.push({
            message: item.constraints[arr],
            field: item.property,
          });
        }
      });
      throw new HttpException(JSON.stringify(result), 400);
    }

    // 自定义验证
    if (value.message != "你好") {
      throw new HttpException("message必须是你好", 400);
    }
    return object;
  }

  private toValidate(metatype: Function): boolean {
    // 白名单
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}
  • (2) 不抛出异常版本

DTO 里面就得改写下

ts
import { IsNotEmpty, IsInt } from "class-validator";

import { CustomNotEmpty } from "./customdto";
export class LoginDto {
  @CustomNotEmpty()
  @IsNotEmpty({ message: "用户名不能为空" })
  username: String;

  @IsInt({ message: "密码只能是数字" })
  password: number;

  // 加一个属性用来判断是否验证通过
  status?: String;
}

管道里面改写

ts
import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  HttpException,
} from "@nestjs/common";

import { plainToInstance } from "class-transformer";

import { validate } from "class-validator";
@Injectable()
export class ValidationPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    const { metatype } = metadata;
    console.log(metatype);
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    // 变成对象,把规则和值 组合成验证的对象
    const object = plainToInstance(metatype, value);
    // 验证 errors 是个数组
    const errors = await validate(object);
    if (errors.length > 0) {
      // 第一种只返回第一个
      if (errors.length == 1) {
        for (let arr in errors[0].constraints) {
          // throw new HttpException(`${errors[0].constraints[arr]}`, 302);
          return {
            code: 200,
            status: "error",
            message: `${errors[0].constraints[arr]}`,
          };
        }
      }
      // 第二种返回所有
      let result = [];
      errors.forEach((item) => {
        for (let arr in item.constraints) {
          result.push({
            message: item.constraints[arr],
            field: item.property,
          });
        }
      });
      return {
        code: 200,
        status: "error",
        message: result,
      };
    }
    return object;
  }

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

使用

ts
import { ValidationPipe2 } from '../pipe/validate2.pipe';
@Post()
async getGuandao2(
  @Body(ValidationPipe2) UserCreateDto: UserCreateDto,
): Promise<string> {
  if(UserCreateDto.status == "error"){
    return UserCreateDto
  }else{
   return this.loginService.getone();
  }
}

全局管道(全局使用)

  • app.module.ts
ts
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { CoreModule } from "./core/core.module";
import { TaskModule } from "./task/task.module";
import { APP_FILTER, APP_PIPE } from "@nestjs/core";
import { HttpExceptionFilter } from "./task/filter/HttpException.filter";
import { ValidationPipe2 } from "./task/pipe/validate2.pipe";
@Module({
  imports: [CoreModule, TaskModule],
  controllers: [],
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
    {
      provide: APP_PIPE,
      useClass: ValidationPipe2,
    },
  ],
})
// 模块实现 NestModule 接口
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // 限定路由
    consumer.apply().forRoutes("*");
  }
}