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 开发中不可或缺的工具。通过合理利用管道,开发者可以专注于业务逻辑的实现,而不必过多担忧底层数据处理的细节,从而加速开发周期,提高软件质量

相关文章
|
2月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
3月前
|
JavaScript
Nest.js 实战 (十一):配置热重载 HMR 给服务提提速
这篇文章介绍了Nest.js服务在应用程序引导过程中,TypeScript编译对效率的影响,以及如何通过使用webpackHMR来降低应用实例化的时间。文章包含具体教程,指导读者如何在项目中安装依赖包,并在根目录下新增webpack配置文件webpack-hmr.config.js来调整HMR相关的配置。最后,文章总结了如何通过自定义webpack配置来更好地控制HMR行为。
|
15天前
Next.js 实战 (二):搭建 Layouts 基础排版布局
本文介绍了作者在Next.js v15.x版本发布后,对一个旧项目的重构过程。文章详细说明了项目开发规范配置、UI组件库选择(最终选择了Ant-Design)、以及使用Ant Design的Layout组件实现中后台布局的方法。文末展示了布局的初步效果,并提供了GitHub仓库链接供读者参考学习。
Next.js 实战 (二):搭建 Layouts 基础排版布局
|
9天前
|
存储 网络架构
Next.js 实战 (四):i18n 国际化的最优方案实践
这篇文章介绍了Next.js国际化方案,作者对比了网上常见的方案并提出了自己的需求:不破坏应用程序的目录结构和路由。文章推荐使用next-intl库来实现国际化,并提供了详细的安装步骤和代码示例。作者实现了国际化切换时不改变路由,并把当前语言的key存储到浏览器cookie中,使得刷新浏览器后语言不会失效。最后,文章总结了这种国际化方案的优势,并提供Github仓库链接供读者参考。
|
10天前
Next.js 实战 (三):优雅的实现暗黑主题模式
这篇文章介绍了在Next.js中实现暗黑模式的具体步骤。首先,需要安装next-themes库。然后,在/components/ThemeProvider/index.tsx文件中新增ThemeProvider组件,并在/app/layout.tsx文件中注入该组件。如果想要加入过渡动画,可以修改代码实现主题切换时的动画效果。最后,需要在需要的位置引入ThemeModeButton组件,实现暗黑模式的切换。
|
25天前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
29 2
|
2月前
|
JavaScript 前端开发 开发者
探索JavaScript原型链:深入理解与实战应用
【10月更文挑战第21天】探索JavaScript原型链:深入理解与实战应用
33 1
|
2月前
|
SQL 前端开发 JavaScript
Nest.js 实战 (十五):前后端分离项目部署的最佳实践
这篇文章介绍了如何使用现代前端框架Vue3和后端Node.js框架Nest.js实现的前后端分离架构的应用,并将其部署到生产环境。文章涵盖了准备阶段,包括云服务器的设置、1Panel面板的安装、数据库的安装、域名的实名认证和备案、SSL证书的申请。在部署Node服务环节,包括了Node.js环境的创建、数据库的配置、用户名和密码的设置、网站信息的填写、静态网站的部署、反向代理的配置以及可能遇到的常见问题。最后,作者总结了部署经验,并希望对读者有所帮助。
203 11
|
2月前
|
存储 JavaScript 前端开发
前端开发:Vue.js入门与实战
【10月更文挑战第9天】前端开发:Vue.js入门与实战
|
1月前
|
前端开发 JavaScript
JavaScript新纪元:ES6+特性深度解析与实战应用
【10月更文挑战第29天】本文深入解析ES6+的核心特性,包括箭头函数、模板字符串、解构赋值、Promise、模块化和类等,结合实战应用,展示如何利用这些新特性编写更加高效和优雅的代码。
45 0