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;
  }
}
目录
相关文章
|
3月前
|
存储 数据管理 API
零代码能力:轻松搞定表单和API接口,少写80%后端代码,内含资源
小白接口(果创云 YesApi.cn)是一个零代码和低代码开发平台,提供一站式后端云服务,帮助开发者、学生、业余爱好者、工作室、中小企业及无IT技术人员的传统企业快速搭建应用、接口、服务和网站。平台提供500+免费API接口,支持在线API开发、在线表单、数据库管理、图片文件存储、会员管理等功能,无需后端开发经验,轻松实现数据处理和应用开发。
|
6月前
|
JSON 数据格式
axios发送post请求,如何接受和返回一个axios的字段,解决bug的方法,困难的事情先从简单做起,先从发送一个axios的post请求做起,解决方法查别人的资料,查看F12看network就行
axios发送post请求,如何接受和返回一个axios的字段,解决bug的方法,困难的事情先从简单做起,先从发送一个axios的post请求做起,解决方法查别人的资料,查看F12看network就行
|
6月前
|
存储 Java
软件开发常用之SpringBoot文件上传接口编写(中),一本书,代码大全(里面有很多代码重构的方法),屎山代码的原因是不断追加逻辑,在错误代码上堆积新的功能,在写完逻辑之后去思考一下,逻辑合理不
软件开发常用之SpringBoot文件上传接口编写(中),一本书,代码大全(里面有很多代码重构的方法),屎山代码的原因是不断追加逻辑,在错误代码上堆积新的功能,在写完逻辑之后去思考一下,逻辑合理不
|
8月前
|
测试技术 数据库 数据安全/隐私保护
阿萨学工具: 你会用Apifox预处理接口的前置操作吗?
阿萨学工具: 你会用Apifox预处理接口的前置操作吗?
563 0
[Nestjs] 手摸手之简单封装API接口的返回结果
介绍:在 NestJs 中,你可以自定义一个统一的响应封装来支持成功、错误和分页响应。
300 0
|
SQL NoSQL Oracle
萌新妹纸不会写后端代码,还不是照样开发API速度贼快
萌新妹纸不会写后端代码,还不是照样开发API速度贼快
288 0
|
安全 API 数据安全/隐私保护
在编写API接口的技术文章时应注意的内容
编写API接口技术文章时建议包含的内容,通过清晰的说明和示例,可以帮助读者准确理解和使用API接口。
|
机器学习/深度学习 JSON 编解码
|
SQL JSON NoSQL
瞧瞧人家写的API接口代码,那叫一个优雅!
瞧瞧人家写的API接口代码,那叫一个优雅!
【宜搭】使用远程API手动或者默认设置中英文(顺便吐槽一下需求提了还浪费时间,因为根本不会做)
在钉钉中使用宜搭,如果是自己发布的应用没有切换语言的按钮。必须到宜搭首页进行切换。这对外贸或者其他有英文需求的行业不是很友好。尤其是上下级组织分发的应用,切换一下语言需要跑到上级组织工作台点一下语言切换才能变为英文。 为此提了需求希望宜搭优化一下,但是然并卵。无用。
【宜搭】使用远程API手动或者默认设置中英文(顺便吐槽一下需求提了还浪费时间,因为根本不会做)