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
    
    AI 代码解读
  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);
    }
    }
    
    AI 代码解读
  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();
    
    AI 代码解读

管道测试

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;
}
AI 代码解读

如果我们提交的请求体中缺少了参数,它就会提示:
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);
  }
}
AI 代码解读

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

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

总结

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

目录
打赏
0
1
1
0
54
分享
相关文章
Next.js 实战 (九):使用 next-auth 完成第三方身份登录验证
这篇文章介绍了next-auth,一个为Next.js设计的身份验证库,支持多种认证方式,如电子邮件和密码、OAuth2.0提供商(如Google、GitHub、Facebook等)以及自定义提供商。文章包含了如何配置Github Provider以及会话管理,并提到了适配器Adapters在next-auth中的作用。最后,文章强调了next-auth的强大功能值得进一步探索。
221 10
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
MutationObserver 是一个非常强大的 API,提供了一种高效、灵活的方式来监听和响应 DOM 变化。它解决了传统 DOM 事件监听器的诸多局限性,通过异步、批量的方式处理 DOM 变化,大大提高了性能和效率。在实际开发中,合理使用 MutationObserver 可以帮助我们更好地控制 DOM 操作,提高代码的健壮性和可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
柯里化是一种强大的函数式编程技术,它通过将函数分解为单参数形式,实现了灵活性与可复用性的统一。无论是参数复用、延迟执行,还是函数组合,柯里化都为现代编程提供了极大的便利。 从 Redux 的选择器优化到复杂的数据流处理,再到深度嵌套的函数优化,柯里化在实际开发中展现出了非凡的价值。如果你希望编写更简洁、更优雅的代码,柯里化无疑是一个值得深入学习和实践的工具。从简单的实现到复杂的应用,希望这篇博客能为你揭开柯里化的奥秘,助力你的开发之旅! 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式
这篇文章介绍了在Next.js框架下,如何处理中后台管理系统中特殊页面(如登录页)不包裹根布局(RootLayout)的问题。作者指出Next.js的设计理念是通过布局的嵌套来创建复杂的页面结构,这虽然保持了代码的整洁和可维护性,但对于特殊页面来说,却造成了不必要的布局包裹。文章提出了一个解决方案,即通过判断页面的skipGlobalLayout属性来决定是否包含RootLayout,从而实现特殊页面不包裹根布局的目标。
160 33
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
这篇文章介绍了作者在使用Nextjs15进行项目开发时遇到的部署问题。在部署过程中,作者遇到了打包构建时的一系列报错,报错内容涉及动态代码评估在Edge运行时不被允许等问题。经过一天的尝试和调整,作者最终删除了lodash-es库,并将radash的部分源码复制到本地,解决了打包报错的问题。文章最后提供了项目的线上预览地址,并欢迎读者留言讨论更好的解决方案。
86 10
Next.js 实战 (五):添加路由 Transition 过渡效果和 Loading 动画
这篇文章介绍了Framer Motion,一个为React设计的动画库,提供了声明式API处理动画和页面转换,适合创建响应式用户界面。文章包括首屏加载动画、路由加载Loading、路由进场和退场动画等主题,并提供了使用Framer Motion和next.js实现这些动画的示例代码。最后,文章总结了这些效果,并邀请读者探讨更好的实现方案。
117 7
Next.js 实战 (六):如何实现文件本地上传
这篇文章介绍了在Next.js中如何实现文件上传到本地的方法。文章首先提到Next.js官方文档中没有提供文件上传的实例代码,因此开发者需要自行实现,通常有两种思路:使用Node.js原生上传或使用第三方插件如multer。接着,文章选择了使用Node.js原生上传的方式来讲解实现过程,包括如何通过哈希值命名文件、上传到指定目录以及如何分类文件夹。然后,文章展示了具体的实现步骤,包括编写代码来处理文件上传,并给出了代码示例。最后,文章通过一个效果演示说明了如何通过postman模拟上传文件,并展示了上传后的文件夹结构。
|
4月前
Next.js 实战 (二):搭建 Layouts 基础排版布局
本文介绍了作者在Next.js v15.x版本发布后,对一个旧项目的重构过程。文章详细说明了项目开发规范配置、UI组件库选择(最终选择了Ant-Design)、以及使用Ant Design的Layout组件实现中后台布局的方法。文末展示了布局的初步效果,并提供了GitHub仓库链接供读者参考学习。
140 1
Next.js 实战 (二):搭建 Layouts 基础排版布局
Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
这篇文章介绍了什么是Next.js中的中间件以及其应用场景。中间件可以用于处理每个传入请求,比如实现日志记录、身份验证、重定向、CORS配置等功能。文章还提供了一个身份验证中间件的示例代码,以及如何使用限流中间件来限制同一IP地址的请求次数。中间件相当于一个构建模块,能够简化HTTP请求的预处理和后处理,提高代码的可维护性,有助于创建快速、安全和用户友好的Web体验。