Nest.js 实战 (四):利用 Pipe 管道实现数据验证和转换

简介: 这篇文章介绍了Nest.js框架中管道的概念和使用。管道是一种强大的功能,用于在请求数据到达控制器方法之前对其进行预处理,如转换、验证、清理等。文章详细解释了数据转换、数据验证、错误处理和一致性等管道的主要用途,并通过代码示例演示了如何使用内置管道和自定义管道。最后,文章总结了管道在提升应用健壮性和安全性方面的作用,认为合理利用管道可以加速开发周期,提高软件质量。

什么是管道(Pipe)?

Nest.js 中,管道(Pipelines) 是一种强大的功能,用于预处理进入控制器方法的请求数据,如请求体、查询参数、路径参数等。管道允许开发者在数据到达控制器方法之前对数据进行转换、验证、清理或执行其他预处理任务。这使得 Nest.js 应用更加健壮、可维护和一致。

以下是 Nest.js 中管道的一些主要用途:

  • 数据转换:管道如 ParseIntPipeParseFloatPipeParseArrayPipe 等可以将原始输入数据转换为应用内部所需的类型,如将字符串转换为整数或浮点数,或将字符串表示的数组转换为数组,保证了数据的一致性和可用性
  • 数据验证:管道可以确保传入的数据符合预期的格式和规则。例如,使用 ValidationPipe 结合 class-validator,可以自动验证请求体或查询参数是否满足特定的 DTO(数据传输对象)定义,从而预防因数据格式错误引起的运行时异常
  • 错误处理:如果数据不符合管道的规则,管道可以抛出异常,从而阻止请求的进一步处理,并向客户端返回适当的错误信息
  • 一致性:管道有助于在整个应用中保持一致性,避免在不同的控制器或方法中重复相同的预处理逻辑
  • 可插拔性和重用性:管道是可插拔的组件,可以很容易地在多个控制器或方法之间共享和重用

内置管道

Nest.js 自带九个开箱即用的管道:

  • ValidationPipe:验证和转换传入的数据。它使用 class-validator 库来检查数据是否符合定义在 DTOs 或请求模型上的验证规则
  • ParseIntPipe:将字符串类型的参数转换为整数
  • ParseFloatPipe:将字符串类型的参数转换为浮点数
  • ParseBoolPipe:将字符串类型的参数转换为布尔值
  • ParseArrayPipe:将字符串形式的数组转换为数组
  • ParseUUIDPipe:解析字符串并验证是否为 UUID
  • ParseEnumPipe:将传入的值转换为枚举类型中的成员
  • DefaultValuePipe:如果传入的参数是 undefinednull,则使用默认值替换它
  • ParseFilePipe:用于处理上传的文件,它可以验证文件的类型、大小等,确保上传的文件符合预期

自定义管道

  1. 安装依赖

    pnpm add class-validator class-transformer
    
  2. 新建 /pipe/validation.pipe.ts 文件:

    import {
         
          ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
    import {
         
          plainToClass } from 'class-transformer';
    import {
         
          validate } from 'class-validator';
    
    @Injectable()
    export class ValidationPipe implements PipeTransform {
         
         
    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]; // 只需要取第一个错误信息并返回即可
       // 自定义校验返回格式
       throw new BadRequestException(`参数校验失败: ${
           
           msg}`);
     }
     return value;
    }
    private toValidate(metatype: any): boolean {
         
         
     const types: any[] = [String, Boolean, Number, Array, Object];
     return !types.includes(metatype);
    }
    }
    
  3. main.ts 中全局注册:

    import {
         
          NestFactory } from '@nestjs/core';
    import {
         
          ValidationPipe } from '@/pipe/validation.pipe'; // 全局管道
    
    import {
         
          AppModule } from './app.module';
    async function bootstrap() {
         
         
    const app = await NestFactory.create(AppModule);
    
    // 全局参数校验
    app.useGlobalPipes(new ValidationPipe());
    
    await app.listen(3000);
    } 
    bootstrap();
    

管道测试

1、假如我们现在有一个添加岗位的接口,它的 DTO 如下:

import {
   
    ApiProperty } from '@nestjs/swagger';
import {
   
    IsNotEmpty, IsNumber, IsOptional, IsUUID } from 'class-validator';

export class SavePostDto {
   
   
  @ApiProperty({
   
   
    type: String,
    description: '父级id',
    default: '0c01ef7d-2f6f-440a-b642-62564d41f473',
    required: false,
  })
  @IsOptional()
  @IsUUID('all', {
   
    message: 'parentId 参数不正确' })
  parentId?: string;

  @ApiProperty({
   
   
    type: String,
    description: '岗位名称',
    default: '前端工程师',
  })
  @IsNotEmpty({
   
    message: '岗位名称必填' })
  name: string;

  @ApiProperty({
   
   
    type: String,
    description: '组织id',
    default: 'f45cd48b-e703-49db-91be-ae7f594e73e0',
  })
  @IsUUID('all', {
   
    message: 'orgId 参数不正确' })
  orgId: string;

  @ApiProperty({
   
   
    type: Number,
    description: '排序',
    default: 1,
  })
  @IsNumber(
    {
   
   },
    {
   
   
      message: '排序必须为数字',
    },
  )
  sort: number;

  @ApiProperty({
   
   
    type: String,
    description: '岗位描述',
    default:
      '前端工程师是互联网时代软件产品研发中不可缺少的一种专业研发角色。从狭义上讲,前端工程师使用 HTML、CSS、JavaScript 等专业技能和工具将产品UI设计稿实现成网站产品,涵盖用户PC端、移动端网页,处理视觉和交互问题。',
    required: false,
  })
  describe?: string;
}

如果我们提交的请求体中缺少了参数,它就会提示:
dqyyvbzjg4q0ewa9y6pxhzyb3dd9jcaf.png

2、假如我们有一个查询岗位详情的接口如下:

import {
   
    Body, Controller, Get, ParseUUIDPipe} from '@nestjs/common';
import {
   
    ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; // swagger 接口文档

import {
   
    ResponseSavePostDto } from './dto/response-post.dto';
import {
   
    PostManageService } from './post-manage.service';

@ApiTags('智能行政-岗位管理')
@Controller('post-manage')
export class PostManageController {
   
   
  constructor(private readonly postManageService: PostManageService) {
   
    }

  /**
   * @description: 查询岗位详情
   */
  @Get(':id')
  @ApiOkResponse({
   
    type: ResponseSavePostDto })
  @ApiOperation({
   
    summary: '查询岗位详情' })
  findOne(@Param('id', new ParseUUIDPipe()) id: string) {
   
   
    return this.postManageService.findOne(id);
  }
}

我们要保证 idUUID,我们就可以使用 ParseUUIDPipe 内置管道,如果参数不对,管道就会给出报错信息:
oqdxf7zofcoy9bcneqwvjs8s8j7leio1.png

这里我们演示了自定义管道和 ParseUUIDPipe 管道的用法,其它内置管道用法也一样,大家可以根据自己的实际情况选择合适的管道。

总结

Nest.js 中的 管道(Pipelines) 不仅简化了数据处理流程,还提升了应用的健壮性和安全性,是现代 Web 开发中不可或缺的工具。通过合理利用管道,开发者可以专注于业务逻辑的实现,而不必过多担忧底层数据处理的细节,从而加速开发周期,提高软件质量

相关文章
|
5天前
|
JavaScript
Nest.js 实战 (十一):配置热重载 HMR 给服务提提速
这篇文章介绍了Nest.js服务在应用程序引导过程中,TypeScript编译对效率的影响,以及如何通过使用webpackHMR来降低应用实例化的时间。文章包含具体教程,指导读者如何在项目中安装依赖包,并在根目录下新增webpack配置文件webpack-hmr.config.js来调整HMR相关的配置。最后,文章总结了如何通过自定义webpack配置来更好地控制HMR行为。
|
25天前
|
缓存 中间件
Nest.js 实战 (九):使用拦截器记录用户 CURD 操作日志
这篇文章介绍了在Nest.js中如何实现记录用户CURD操作的需求。首先解释了什么是拦截器以及拦截器的作用,然后通过创建Prisma模型,添加Log模型,并通过编写LoggerInterceptor拦截器,实现了记录用户操作的功能。最后通过效果演示和总结,强调了使用拦截器实现此功能的有效性。
|
8天前
|
SQL 运维 监控
Nest.js 实战 (十):使用 winston 打印和收集日志记录
这篇文章介绍了在Nest服务中如何使用Winston记录日志。文章首先强调了日志记录在后台服务中的重要性,接着提到Nest默认的内部日志记录器,并指出可以通过@nestjs/common包中的Logger类来全面控制日志系统的行为。文章还提到,为了在生产环境中实现更高级的日志功能,可以使用如Winston之类的Node.js日志包。接下来,文章介绍了如何在Nest服务中使用Winston记录日志,包括安装相关依赖、创建winston配置文件以及实现简单的日志记录示例。最后,文章指出更高级的自定义日志功能需要读者自己去探索。
Nest.js 实战 (十):使用 winston 打印和收集日志记录
|
23天前
|
JavaScript 前端开发
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
这篇文章是JavaScript基础与实战教程的第一部分,涵盖了JavaScript的基本语法、标识符、数据类型以及如何进行强制类型转换,通过代码示例介绍了JS的输出语句、编写位置和数据类型转换方法。
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
|
1月前
|
JavaScript
Nest.js 实战 (七):如何生成 SVG 图形验证码
这篇文章介绍了使用NestJS实现Session验证的图形验证码功能的具体步骤。首先,通过powershell代码安装依赖pnpmaddsvg-captcha。然后,在控制器中使用TypeScript代码引入相关依赖,创建一个图形验证码的接口,当请求该接口时,返回一张随机图片验证码。最后,进行了效果演示。
Nest.js 实战 (七):如何生成 SVG 图形验证码
|
12天前
|
JavaScript 数据安全/隐私保护 Python
网易云音乐搜索接口JS逆向: Params、encSecKey加密和AES实战
网易云音乐搜索接口JS逆向: Params、encSecKey加密和AES实战
18 4
|
9天前
|
前端开发 JavaScript 开发者
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
本文提供了一个前端JS按钮点击事件、弹出式窗口和遮罩层的实战示例,包括HTML、CSS和JavaScript的具体实现代码,以及功能解析,演示了如何实现按钮点击后触发弹窗显示和遮罩层,并在2秒后自动关闭或点击遮罩层关闭弹窗的效果。
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
|
12天前
|
JavaScript 前端开发 安全
JS 混淆解析:JS 压缩混淆原理、OB 混淆特性、OB 混淆JS、混淆突破实战
JS 混淆解析:JS 压缩混淆原理、OB 混淆特性、OB 混淆JS、混淆突破实战
24 2
|
23天前
|
JavaScript 前端开发
JavaScript基础&实战 JS中正则表达式的使用
这篇文章介绍了JavaScript中正则表达式的使用,包括正则表达式的创建、匹配模式、字符串匹配、拆分、搜索、匹配和替换等方法,并通过示例代码展示了如何应用这些技术。
JavaScript基础&实战 JS中正则表达式的使用
|
23天前
|
JavaScript 前端开发
JavaScript基础&实战(5)js中的数组、forEach遍历、Date对象、Math、String对象
这篇文章介绍了JavaScript中的数组、Date对象、Math对象以及包装类(String、Number、Boolean),并详细讲解了数组的创建、方法(如forEach、push、pop、unshift、slice、splice)和遍历操作,以及工厂方法创建对象和原型对象的概念。
JavaScript基础&实战(5)js中的数组、forEach遍历、Date对象、Math、String对象