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;
  }
}
目录
相关文章
|
前端开发 JavaScript 容器
【实战】用CSS实现文本打字机效果
【实战】用CSS实现文本打字机效果
1609 0
【实战】用CSS实现文本打字机效果
|
Docker 容器
error during connect: this error may indicate that the docker daemon is not running
error during connect: this error may indicate that the docker daemon is not running
779 0
|
6月前
|
Java API 数据安全/隐私保护
访问修饰符 public private protected 及默认情况的区别解析
在Java编程中,访问修饰符(`public`、`private`、`protected`和默认)用于控制类、方法、字段及构造函数的访问范围。`public`允许所有类访问;`private`仅限类内部访问;`protected`允许同一包内或子类访问;默认(无修饰符)仅限同一包内访问。通过合理使用这些修饰符,可实现数据封装、提高安全性和代码可维护性。了解它们的区别与应用场景,是掌握Java面向对象编程的关键。
1079 6
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
350 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
Python
【Python】解决Can‘t find model ‘en‘. It doesn‘t seem to be a shortcut link, a Python package or a valid
在使用以下代码时,报错Can’t find model ‘en’. It doesn’t seem to be a shortcut link, a Python package or a valid path to a data directory.
228 1
|
机器学习/深度学习 自然语言处理 PyTorch
【传知代码】transformer-论文复现
Transformer模型,革新NLP的里程碑,摒弃CNN/RNN,采用自注意力机制解决长距离依赖问题,加速训练。模型包含编码器和解码器,用位置编码补充序列信息。关键组件包括:嵌入层(位置编码增强词向量)、多头注意力层(捕获不同侧面的上下文)、前馈层(非线性变换)及残差连接和层归一化(提升训练稳定性)。Transformer的创新应用,推动了现代大语言模型的发展。
756 1
【传知代码】transformer-论文复现
|
Linux 开发工具
在Linux中,如何创建一个新的分区并格式化为EXT4文件系统?
在Linux中,如何创建一个新的分区并格式化为EXT4文件系统?
|
Ubuntu 网络安全 数据安全/隐私保护
ubuntu篇-配置FTP服务,本机和docker安装
通过以上步骤,你可以在Ubuntu上配置FTP服务,无论是本机安装还是Docker内安装,都可以提供FTP文件传输服务。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
519 1
|
安全 网络协议 Go
gRPC- HTTP网关 I
gRPC- HTTP网关 I
329 0
|
Java API 开发者
springboot 多线程的使用原理与实战
在Spring Boot中实现多线程,主要依赖于Spring框架的@Async注解以及底层Java的并发框架。这里将深入剖析Spring Boot多线程的原理,包括@Async注解的工作方式、任务执行器的角色以及如何通过配置来调整线程行为。
1262 5