NestJS 7.x 折腾记: 管道,一个好玩的东西!比如入参校验!

Posted crper

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NestJS 7.x 折腾记: 管道,一个好玩的东西!比如入参校验!相关的知识,希望对你有一定的参考价值。

前言


管道这个词,若是有玩过Linux的小伙伴肯定知晓,比如(看图):

意思就git的提交记录以单行显示的前三条,而且提交概要中有build这个词的~


在nest里面的管道,主要是用来做入参数据类型的拦截转换;
跟ng提供的概念差不多,大同小异~~



效果图

实战

其实官方教程写的挺好了,
局部管道和全局管道都写到了,
这里我们以更具通用性的入参全局管道做例子,
尽量写的更贴近业务和更为粗俗的解释吧~

安装

# class-validator: 提供非常丰富的类型装饰器
# class-transformer: 可以把常规数据快速转成类~
# https://www.npmjs.com/package/class-validator
# https://www.npmjs.com/package/class-transformer
yarn add class-validator class-transformer

封装入参拦截管道

Usage: nest <command> [options]

Options:
  -v, --version                                   Output the current version.
  -h, --help                                      Output usage information.

Commands:
  new|n [options] [name]                          Generate Nest application.
  build [options] [app]                           Build Nest application.
  start [options] [app]                           Run Nest application.
  info|i                                          Display Nest project details.
  update|u [options]                              Update Nest dependencies.
  add [options] <library>                         Adds support for an external library to your project.
  generate|g [options] <schematic> [name] [path]  Generate a Nest element.
    Available schematics:
      ┌───────────────┬─────────────┬───────────────────────────────���──────────────┐
      │ name          │ alias       │ description                                  │
      │ application   │ application │ Generate a new application workspace         │
      │ class         │ cl          │ Generate a new class                         │
      │ configuration │ config      │ Generate a CLI configuration file            │
      │ controller    │ co          │ Generate a controller declaration            │
      │ decorator     │ d           │ Generate a custom decorator                  │
      │ filter        │ f           │ Generate a filter declaration                │
      │ gateway       │ ga          │ Generate a gateway declaration               │
      │ guard         │ gu          │ Generate a guard declaration                 │
      │ interceptor   │ in          │ Generate an interceptor declaration          │
      │ interface     │ interface   │ Generate an interface                        │
      │ middleware    │ mi          │ Generate a middleware declaration            │
      │ module        │ mo          │ Generate a module declaration                │
      │ pipe          │ pi          │ Generate a pipe declaration                  │
      │ provider      │ pr          │ Generate a provider declaration              │
      │ resolver      │ r           │ Generate a GraphQL resolver declaration      │
      │ service       │ s           │ Generate a service declaration               │
      │ library       │ lib         │ Generate a new library within a monorepo     │
      │ sub-app       │ app         │ Generate a new application within a monorepo │
      │ resource      │ res         │ Generate a new CRUD resource    
# 会在src/common/pipes/生成  validation.pipe.ts
# 根路径在nest-cli.json配置!默认就是src为开发根目录

nest n pi common/pipes/validation

管道实现

// src/pipe/validation.pipe.ts
import 
  ArgumentMetadata,
  BadRequestException,
  Injectable,
  PipeTransform,
 from '@nestjs/common';
// plainToClass 会把一个普通的js对象转换成指定类的实例
import  plainToClass  from 'class-transformer';
// 可以识别校验装饰器数据
import  validate  from 'class-validator';

Injectable();
export class ValidationPipe implements PipeTransform 
  // value 就是传入的实际数据
  // metatype 就是元数据,其实就是装饰器添加那些
  async transform(value: any,  metatype : ArgumentMetadata) 
    if (!metatype || !this.toValidate(metatype)) 
      // 如果没有传入验证规则,则不验证,直接返回数据
      return value;
    
    // 将对象转换为 Class 来验证
    const object = plainToClass(metatype, value);
    // 同步阻塞,返回校验结果
    const errors = await validate(object);
    if (errors.length > 0) 
      // 只需要取第一个错误信息并返回即可
      const msg = Object.values(errors[0].constraints)[0]; 
      // 抛出这个异常,逻辑就会交付nest的错误拦截去了
      // 要拦截这个错误做处理,可以从filters入手,以后会说到
      throw new BadRequestException(`字段校验不通过: $msg`);
    
    return value;
  
  // 这个函数的意义就是验证元数据传入的类型是否是定义内的常规类型数据
  private toValidate(metatype: any): boolean 
    const types: any[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  


配置

主入口(main.ts)

import  AppModule  from './app.module';
import  NestFactory  from '@nestjs/core';
import  ValidationPipe  from './common/pipes/validataion.pipe';
async function bootstrap() 
  const app = await NestFactory.create(AppModule, 
    cors: false,
    logger: false,
  );

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

  await app.listen(configService.get('SERVE_LISTENER_PORT'));

bootstrap()

DTO注解

import  ApiProperty, ApiPropertyOptional  from '@nestjs/swagger';
import 
  IsInt,
  IsNumberString,
  IsOptional,
  IsString,
  Max,
  Min,
 from 'class-validator';

export enum UserRole 
  Boss = '后门',
  Admin = '管理员',
  User = '常规用户',


export class CreateAppDto 
  @ApiProperty( enum: ['Boss', 'Admin', 'User'] )
  role: UserRole;

  @IsOptional()
  @IsString(
    message: '用户名必须为字符串',
  )
  @ApiPropertyOptional(
    description: '姓名',
  )
  readonly name?: string;

  @IsInt()
  @Min(10,  message: '年龄下限为10' )
  @Max(130,  message: '年龄上限130' )
  @ApiProperty(
    description: '年龄',
    minimum: 0,
    maximum: 130,
    required: false,
  )
  readonly age: number;

  @IsString(
    message: '爱好必须为字符串',
  )
  @ApiPropertyOptional(
    description: '爱好',
  )
  readonly hobit: string;


export class FindOneParams 
  @IsNumberString()
  id: number;

Controller

import  Controller, Get, Post, HttpCode, Body, Query  from '@nestjs/common';
import 
  ApiCreatedResponse,
  ApiHeader,
  ApiInternalServerErrorResponse,
  ApiOkResponse,
  ApiOperation,
  ApiParam,
  ApiQuery,
  ApiResponse,
 from '@nestjs/swagger';
import  CreateAppDto, FindOneParams, UserRole  from './app.dto';
import  AppService  from './app.service';

@Controller()
export class AppController 
  constructor(private readonly appService: AppService) 

  @Get()
  getHello(): string 
    return this.appService.getHello();
  

  @ApiHeader(
    name: 'Authorization',
    description: 'Auth token',
  )
  @ApiCreatedResponse(
    description: '链接成功创建,其实就是201状态的描述',
  )
  @Post('/post')
  @HttpCode(200)
  @ApiParam( name: 'name', description: '名字', type: CreateAppDto )
  postParams(@Body() param: CreateAppDto): string 
    return '测试参数' + JSON.stringify(param);
  

  @Get('/user')
  @ApiOperation(
    tags: ['获取用户信息'],
    description: '获取用户信息',
    deprecated: true,
  )
  @ApiQuery( name: 'id', description: '用户id' )
  @ApiResponse( description: '成功请求回来,其实就是200的描述', status: 200 )
  @ApiInternalServerErrorResponse( description: '服务端异常' )
  updateApp(@Query() query: FindOneParams) 
    return JSON.stringify(query);
  

  @Get('/netease-news/:id')
  @ApiOkResponse( description: '成功请求回来' )
  @ApiQuery( name: 'id', description: '用户id', required: false )
  async async(@Body() body) 
    const res = await this.appService.getNetEaseNew(
      'https://anapioficeandfire.com/api/characters/583',
       data: body ,
    );
    return res.data;
  

  @ApiQuery( name: 'role', enum: UserRole )
  @ApiOperation(
    tags: ['返回角色信息'],
    description: '返回角色信息',
  )
  @Get('/role')
  async filterByRole(@Query('role') role: UserRole = UserRole.User) 
    return role;
  


总结

有不对之处请留言,会及时修正!谢谢阅读~

以上是关于NestJS 7.x 折腾记: 管道,一个好玩的东西!比如入参校验!的主要内容,如果未能解决你的问题,请参考以下文章

CentOS上oracle 11g R2数据库安装折腾记

VS Code 折腾记 - (19) 一些相对实用的编码体验插件(偏前端)

在 nestjs 中使用验证管道给了我一个 classTransformer.plainToclass is not a function 错误

Atom编辑器折腾记

(汇总)Spring Boot 实践折腾记 & Spring Boot 2.x 实践记

[转] webpack折腾记:性能优化