NestJs 管道(Pipe)

这篇具有很好参考价值的文章主要介绍了NestJs 管道(Pipe)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🎄Hi~ 大家好,我是小鑫同学,资深 IT 从业者,InfoQ 的签约作者,擅长前端开发并在这一领域有多年的经验,致力于分享我在技术方面的见解和心得

🚀技术&代码分享

  • 我在 94Code 总结技术学习;
  • 我在 1024Code 在线编写代码;
  • 我在 Github 参与开源学习;

😇推荐几个好用的工具

  • var-conv 适用于VSCode IDE的代码变量名称快速转换工具
  • generator-vite-plugin 快速生成Vite插件模板项目
  • generator-babel-plugin 快速生成Babel插件模板项目

进入正题

Nestjs 中管道是具有 @Injectable() 装饰器且已实现 PipeTransform 接口的类。

管道(Pipe)的作用

管道(Pipe)作用在每个控制器的处理方法上,也就是当每一个请求被路由到具体的控制器的方法后会先通过管道(Pipe)对传入的请求参数进行 转换验证,保证数据在被正式处理前是完全合法的。

管道(Pipe)的使用

Nestjs 中内置了下列的9个管道,利用这些管道可以轻松的验证路由参数、查询参数和请求正文是否合法,下面通过两个例子一起看一下管道的使用。

ParseIntPipe ParseFloatPipe
ParseBoolPipe ParseArrayPipe
ParseUUIDPipe ParseEnumPipe
ParseFilePipe
DefaultValuePipe ValidationPipe

findUserById 是用来根据用户 ID 获取用户信息的处理函数,期望id由客户端传来的必须是数字类型。

@Controller('users')
export class UsersController {
  @Get(':id')
  findUserById(@Param('id') id: number): string {
    return `The ID of this user is ${id}`;
  }
}

现在由于缺少对路由参数类型的校验,此时客户端在传递非数字类型的ID时并不会收到合理的提醒,这样很容易造成服务端业务逻辑的异常,有入库的操作的话还会造成垃圾数据。所以可将 ParseIntPipe 管道类直接添加到 @Param() 装饰器的第二位参数,如下图:

@Controller('users')
export class UsersController {
  @Get(':id')
  findUserById(@Param('id', ParseIntPipe) id: number): string {
    return `The ID of this user is ${id}`;
  }
}

增加 ParseIntPipe 管道的限制后,当客户端再次传递非数字类型的ID时就会收到对应的提示。

NestJs 管道(Pipe)

上面的例子中使用了管道类而非管道的实例是因为 Nestjs 基于 IoC 的设计在框架内部可以自动对类进行实例化操作,管道同时也支持通过构造函数传递选项的方式自定义内置管道的行为。

下面这个 findUserByUUID 函数中使用的 ParseUUIDPipe 管道默认情况下是支持接收不同版本的 UUID 的,但在例子中我们限制只可以接收 v5 版本的 UUID,就需要实例化 ParseUUIDPipe 并在构造函数中指定具体的 version

@Get(':uuid')
findUserByUUID(
    @Param('uuid', new ParseUUIDPipe({ version: '5' })) uuid: string,
): string {
    return `The UUID of this user is ${uuid}`;
}

NestJs 管道(Pipe)

NestJs 管道(Pipe)

基于 schema 的验证

createUser 处理函数中要求客户端传递一份包含 nameagegender 的数据,对于这种复杂的数据结构来说可以引入 schema (前端表单校验常用技术)来配合自定义管道实现。

export class CreateUserDto {
  name: string;
  age: number;
  gender: boolean;
}

@Post()
createUser(@Body() createUserDto: CreateUserDto): string {
  return `${createUserDto.name} is the 100th user`;
}

首先需要引入 joi 模块和 @types/joi 模块,使用 ES 模块导入的方式导入 joi 时需要在 tsconfig.json 中启用 esModuleInterop 选项。接着使用 Joi 模块将 CreateUserDto 中的三个属性均设置为必填项。

import Joi from 'joi';

export const createUserSchema = Joi.object({
  name: Joi.string().required(),
  age: Joi.number().required(),
  gender: Joi.bool().required(),
});

定义完 schema 后可以使用 nest g pi joi-validation 创建一个公共的管道,在 transform 函数中使用已经注入的ObjectSchema 对象提供的 validate 函数对请求参数 value 做验证,当验证不通过是抛出合理的异常,反之通过。

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

这里的管道就需要绑定到 createUser 处理函数级别了,需要用到 @UsePipes() 装饰器,并传入通过 Joi 定义的 schema

@Post()
@UsePipes(new JoiValidationPipe(createUserSchema))
createUser(@Body() createUserDto: CreateUserDto): string {
  return `${createUserDto.name} is the 100th user`;
}

当客户端未传递其中某一个字段时就会收到如下的提示信息。

NestJs 管道(Pipe)

基于 dto 的验证

在基于 schema 的验证中不仅编写了通用的 joi-validation 管道,还用 Joi 库编写了一份和 CreateUserDto 几乎一样的 schema 文件,每当 DTO 文件有变更时就需要同步维护 schema 文件。

基于 dto 的验证就可以利用为已创建的 CreateUserDto 增加验证相关的装饰器并配合通过的管道即可完成,从而可以少维护一份文件,避免不一致造成的问题。

首先执行 npm i --save class-validator class-transformer 安装必要的模块,接着为 CreateUserDto 增加验证相关的装饰器。

import { IsString, IsNumber, IsBoolean, IsNotEmpty } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  name: string;

  @IsNumber()
  @IsNotEmpty()
  age: number;

  @IsBoolean()
  @IsNotEmpty()
  gender: boolean;
}

接着执行 nest g pi dto-validation 创建一个公共的管道,在这个管道中需要做这么几件事情:

  1. 解构 metadata 参数,获取请求体参数的元类型。
  2. 定义私有函数 toValidation,跳过非DTO的类型(非Javascript原类型)。
  3. 使用 plainToInstance 将元类型和请求体参数转为可验证的类型对象。
  4. 通过 validate 函数执行校验,校验未通过则抛出合理的异常信息。
import {
  ArgumentMetadata,
  BadRequestException,
  Injectable,
  PipeTransform,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';

@Injectable()
export class DtoValidationPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    const { metatype } = metadata;
    if (!metatype || !this.toValidation(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  /**
   * 当 metatype 所指的参数的元类型仅为Javascript原生类型的话则跳过校验,这里只关注了对定义的DTO的校验
   */
  private toValidation(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

再接着将 DtoValidationPipe 管道绑定到 createUser 处理方法并作验证。

@Post()
createUser(
  @Body(new DtoValidationPipe()) createUserDto: CreateUserDto,
): string {
  return `${createUserDto.name} is the 100th user`;
}

NestJs 管道(Pipe)

PS:Nestjs 提供的 ValidationPipe 管道可以完全支持上述两种验证方式,我们不必为自定义验证管道花费时间。

提供默认值

提供默认值可以看做是管道在转换场景的一个体现,增加默认值的处理可以使得服务端的代码更加的健壮。这里使用到了内置的 DefaultValuePipe 管道。

@Get()
findAllUsers(
  @Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe)
  activeOnly: boolean,
  @Query('page', new DefaultValuePipe(10), ParseIntPipe) page: number,
): string {
  return `This action return all users,request parameters:activeOnly: ${activeOnly},page:${page}`;
}

NestJs 管道(Pipe)

全局管道注册

除上述管道的注册位置,还支持全局注册,注册方式同全局异常过滤器的注册,一个是基于 app 实例的注册,另一个是基础跟模块的注册。

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

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

总结

以上就是 Nest 中管道类的使用方式,也是保证参数正常接收、正常入库的必要手段。


如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~文章来源地址https://www.toymoban.com/news/detail-484247.html

到了这里,关于NestJs 管道(Pipe)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java.io.IOException: Broken pipe管道断开

    一、Broken pipe产生原因分析 1.当访问某个服务突然服务器挂了,就会产生Broken pipe; 2.客户端读取超时关闭了连接,这时服务器往客户端再写数据就发生了broken pipe异常! 3.端口冲突,地址已被使用,也会导致Broken pipe 二、方案 1.问题一分析服务器为什么挂了。 2.问题二使用jp

    2024年02月10日
    浏览(50)
  • Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列

    什么是进程间通信,就是进程与进程之间进行通信,互相发送消息;可以通过 信号 或者 管道 或者 消息队列 或者 信号量 去通信! 目录 一、信号 1. 信号简介  2. 都有那些信号? 3. 注册信号的函数 1). signal 2). sigaction (项目中强烈推荐使用) 4. 信号发送 1). kill 函数 2). alarm 函

    2024年02月01日
    浏览(40)
  • 【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

    当今计算机系统中,进程间通信扮演着至关重要的角色。随着计算机系统的发展和复杂性的增加,多个进程之间的协作变得更加必要和常见。进程间通信使得不同进程能够共享资源、协调工作、传输数据,并实现更加复杂和强大的功能。本文将深入探讨进程间的通信,以及管

    2024年02月05日
    浏览(72)
  • 在Python中优雅地用多进程:进程池 Pool、管道通信 Pipe、队列通信 Queue、共享内存 Manager Value

    Python 自带的多进程库 multiprocessing 可实现多进程。我想用这些短例子示范如何优雅地用多线程。中文网络上,有些人只是翻译了旧版的 Python 官网的多进程文档。而我这篇文章会额外讲一讲下方加粗部分的内容。 创建进程 Process, fork 直接继承资源,所以初始化更快,spawn 只

    2024年02月16日
    浏览(41)
  • 小满nestjs(第一章 介绍nestjs)

    视频课程 小满nest js 系列_哔哩哔哩_bilibili Nestjs 是一个用于构建高效可扩展的一个基于Node js 服务端 应用程序开发框架 并且完全支持typeScript  结合了 AOP 面向切面的编程方式 nestjs 还是一个spring MVC 的风格 其中有依赖注入 IOC 控制反转 都是借鉴了Angualr nestjs 的底层代码运用了

    2024年02月01日
    浏览(37)
  • NestJS入门及实战(一)NestJS入门、RESTful API

    Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架 JS 届的 Spring 框架 支持 TypeScript 语言 构建高效可伸缩 装饰器风格 模块加载采用依赖注入 IOC 方式 (Spring 与 AngularJS) 配套功能齐备(鉴权、文档、微服务、CLI、Graph QL) 可能Node大家了解最多的后

    2023年04月08日
    浏览(28)
  • NestJS 基础概念

      1. Module Module是NestJS 的基本组织单位。 模块系统基于 Node.js 的 CommonJS 模块系统,但提供了更高级别的抽象和组织方式。通过使用模块,你可以将应用程序拆分成多个独立且可复用的部分,每个模块都负责实现特定的功能或业务逻辑。 模块可以封装相关的代码、配置和依赖

    2024年04月08日
    浏览(31)
  • nestjs笔记

    控制反转 IoC 控制反转(Inversion of Control,缩写为 IoC)是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的

    2024年02月03日
    浏览(34)
  • nestjs 装饰器

    装饰器是一种特殊的类型声明,它可以附加在类、方法、属性、参数上边 需开启tsconfig.json中 \\\"experimentalDecorators\\\":true 生成tsconfig.json文件 2、类装饰器 2、属性装饰器 3、参数装饰器 4、方法装饰器 5、自定义装饰器 添加配置、安装依赖

    2024年01月16日
    浏览(58)
  • 利用Golang pipe实现远程交互

    本文介绍Golang pipe,以及在不同场景下的应用。 pipe实现从一个进程重定向至另一个进程,它是双向数据通道,用于实现进行间通信。 io.Pipe函数创建内存同步通道,用于连接io.Reader和io.Writer. 本文示例使用环境为: 在实现远程交互之前,先看下面简单示例,演示如何使用io.Pi

    2024年02月02日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包