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

简介: 管道这个词,若是有玩过Linux的小伙伴肯定知晓,比如(看图):意思就git的提交记录以单行显示的前三条,而且提交概要中有build这个词的~在nest里面的管道,主要是用来做入参数据类型的拦截转换;跟ng提供的概念差不多,大同小异~~

网络异常,图片无法展示
|


前言


管道这个词,若是有玩过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;
  }
}
目录
相关文章
|
5月前
|
JSON 数据格式
axios发送post请求,如何接受和返回一个axios的字段,解决bug的方法,困难的事情先从简单做起,先从发送一个axios的post请求做起,解决方法查别人的资料,查看F12看network就行
axios发送post请求,如何接受和返回一个axios的字段,解决bug的方法,困难的事情先从简单做起,先从发送一个axios的post请求做起,解决方法查别人的资料,查看F12看network就行
|
5月前
|
存储 Java
软件开发常用之SpringBoot文件上传接口编写(中),一本书,代码大全(里面有很多代码重构的方法),屎山代码的原因是不断追加逻辑,在错误代码上堆积新的功能,在写完逻辑之后去思考一下,逻辑合理不
软件开发常用之SpringBoot文件上传接口编写(中),一本书,代码大全(里面有很多代码重构的方法),屎山代码的原因是不断追加逻辑,在错误代码上堆积新的功能,在写完逻辑之后去思考一下,逻辑合理不
|
7月前
|
JSON 程序员 API
现在的程序员真是越来越懒了,api 文档都懒得写!程序员:“api工具惯的”
现在的程序员真是越来越懒了,api 文档都懒得写!程序员:“api工具惯的”
[Nestjs] 手摸手之简单封装API接口的返回结果
介绍:在 NestJs 中,你可以自定义一个统一的响应封装来支持成功、错误和分页响应。
287 0
|
SQL NoSQL Oracle
萌新妹纸不会写后端代码,还不是照样开发API速度贼快
萌新妹纸不会写后端代码,还不是照样开发API速度贼快
283 0
|
机器学习/深度学习 JSON 编解码
|
SQL JSON NoSQL
瞧瞧人家写的API接口代码,那叫一个优雅!
瞧瞧人家写的API接口代码,那叫一个优雅!
|
JSON 前端开发 数据格式
#yyds干货盘点 【React工作记录十二】前端对接口参数错误如何解决
#yyds干货盘点 【React工作记录十二】前端对接口参数错误如何解决
171 0
|
前端开发 数据管理 测试技术
用过这个API接口工具后,确实感觉postman有点鸡肋......
这果真是国人开发出来的软件,真的是懂自己人。
用过这个API接口工具后,确实感觉postman有点鸡肋......
|
前端开发
前端工作总结252-uni-时间过滤器封装
前端工作总结252-uni-时间过滤器封装
88 0
前端工作总结252-uni-时间过滤器封装