nest 管道

时间:2023-01-07 浏览:191 分类:NestJS

作用

一般在控制器中引入使用,作用有两个 - 

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

  • 数据校验:校验输入数据,如果有效的,则简单地通过它;否则,当数据不正确时抛出异常

在这两种情况下,管道都对控制器路由处理程序处理的参数进行操作。 Nest 在调用方法之前插入一个管道,管道接收指定给该方法的参数并对它们进行操作。任何转换或验证操作都会在此时发生,之后会使用任何(可能)转换的参数调用路由处理程序。

nest 内置的管道类有:

通过 @nestjs/common 包引入

ValidationPipe
ParseIntPipe
ParseFloatPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
ParseEnumPipe
DefaultValuePipe
ParseFilePipe

类型转换

方法一:

// user.controller.ts

import {Controller, Get, Param, ParseIntPipe} from '@nestjs/common';

@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) {
    }

    @Get('/:id')
    test(@Param('id', ParseIntPipe) id: number) {
        console.log(typeof id);  // number 类型
        return 'test';
    }

}

还可以传入异常处理参数

@Get('/:id')
test(@Param('id', new ParseIntPipe({errorHttpStatusCode:HttpStatus.NOT_ACCEPTABLE})) id: number) {
    console.log(typeof id);
    return 'test';
}

这样如果输入数据错误,nest 会直接抛出异常以及错误代号,可以考虑通过Exception filters 捕获异常

{
  "statusCode": 406,
  "message": "Validation failed (numeric string is expected)",
  "error": "Not Acceptable"
}

方法二:

使用UsePipes引入管道:

@Get('/:id')
@UsePipes(new ParseIntPipe({errorHttpStatusCode:HttpStatus.NOT_ACCEPTABLE}))  //效果一样
test(@Param('id') id: number) {
    console.log(typeof id);
    return 'test';
}

数据转换校验:

第一步: 安装依赖

$ npm i --save class-validator class-transformer

第二步:定义数据类型Dto

//person.dto.ts

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

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

    @IsInt()
    @Transform(value => parseInt(value.value))   //自动类型转换将“age”自动转换为数字类型
    age: number;
}

第三步:引入校验

// user.controller.ts

import {Controller, Get, Post, Query, UsePipes, ValidationPipe} from '@nestjs/common';
import {UserService} from './user.service';
import {Person} from "./Dto/person.dto";

@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) {
    }

    @Get('/')
    index(@Query(new ValidationPipe({transform:true})) query: Person) {
        return this.userService.index(query);
    }
    
    // 或者使用UsePipes装饰器引入
    @Get('/')
    @UsePipes(new ValidationPipe({transform: true}))
    index(@Query() query: Person) {
        return this.userService.index(query);
    }
}

注意:这里传入transform: true 参数是因为在dto中需要将age类型从string自动转换为 数字类型,如果校验中不需要数据类型转换,则不需要传入此参数。

ValidationPipe 可以传入的参数有

export interface ValidationPipeOptions extends ValidatorOptions {
  transform?: boolean;   // 是否开启类型转换
  disableErrorMessages?: boolean;   // 是否禁用详细错误
  exceptionFactory?: (errors: ValidationError[]) => any;}
}

使用Joi库进行类数据验证

除了上述验证,如果输入的数据是类class,也可以通过第三方Joi进行验证。

第一步:安装依赖

$ npm install --save joi

第二步:定义类模型和Dto

// animal.dto.ts

import * as Joi from "joi";

export const AnimalSchema = Joi.object({
    sex: Joi.string().required(),
    age: Joi.number()
        .min(3)
        .max(10)
        .required(),
});

export class Animal {
    sex: string;
    age: number;
}

第三步:自定义validation 管道逻辑

//joi.pipe.ts

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ObjectSchema } from '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;
    }
}

第四步:引入管道到控制器

// user.controller.ts

import {Animal, AnimalSchema} from "./Dto/animal";
import {JoiValidationPipe} from "./validation.pipe";

@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) {
    }
    
    @Get('/animal')
    @UsePipes(new JoiValidationPipe(AnimalSchema))
    animal(@Query() query: Animal) {
        console.log(query);
        return 'animal';
    }
}

如果请求输入数据不合法,同样会抛出400 BadRequest 异常

自定义管道

// parse-int.pipe.ts

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;
  }}

全局引入管道

方法一:在main.ts app下引入

//main.ts

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

方法二:模块中引入:

// app.module.ts

import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

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