1. 前言2. 内置管道2.1 ValidationPipe2.2 ParseIntPipe2.3 ParseFloatPipe2.4 ParseBoolPipe2.5 ParseArrayPipe2.6 ParseUUIDPipe2.7 ParseEnumPipe2.8 DefaultValuePipe2.9 ParseFilePipe3. ValidationPipe3.1 class-validator3.2 class-transformer3.3 全局使用验证管道3.4 添加验证规则3.5 测试一下验证管道是否有效3.6 删除不必要的属性4. 转换输入参数的数据类型5. 自定义管道5.1 定义管道5.2 使用管道6. 总结
1. 前言
在上一节中提到了 dto 文件,它用于定义请求参数的类型,这是对数据输入的一种验证保护,使其符合 API 规范。在 NestJS 中提供了管道的特性,允许路由处理器通过管道对输入参数进行验证或是转换:
- 验证:评估输入数据,如果有效,只需将其原样传递;否则抛出异常
- 转换:将输入数据转换为所需的形式(例如:从字符串到整数)
具体来说,管道将会在路由处理器的
arguments 上运行,Nest 在调用方法之前插入一个管道,管道接收指定给该方法的参数并对它们进行操作,然后使用验证或转换过的参数去调用路由处理程序。⚠️ 注意:管道虽然类似于中间件,但是只负责处理输入参数!因此,提到管道的地方就要理解是要处理输入参数了。
假设有路由 /example,下面是验证管道的工作原理:

2. 内置管道
NestJS 内置了一些常见的管道。
2.1 ValidationPipe
ValidationPipe:这是一个通用的管道,用于验证传入的数据。它可以与 class-validator 包一起使用,自动验证 DTO 对象的属性,确保它们符合定义的验证规则。whitelist:忽略 DTO 中未定义的属性。
forbidNonWhitelisted:如果请求体中包含未定义的属性,抛出异常。
curl 请求示例:
2.2 ParseIntPipe
ParseIntPipe:将传入的字符串转换为整数,如果转换失败,它将返回一个错误。curl 请求示例:
2.3 ParseFloatPipe
ParseFloatPipe:类似于 ParseIntPipe,但用于将字符串转换为浮点数。curl 请求示例:
2.4 ParseBoolPipe
ParseBoolPipe:将传入的值转换为布尔值,可以处理字符串(如 "true", "false", "1", "0")和其他表示真值或假值的输入。curl 请求示例:
2.5 ParseArrayPipe
ParseArrayPipe:将传入的字符串转换为数组。curl 请求示例:
2.6 ParseUUIDPipe
ParseUUIDPipe:将传入的字符串转换为 UUID 对象,如果字符串不是有效的 UUID,它将返回一个错误。curl 请求示例:
2.7 ParseEnumPipe
ParseEnumPipe:将传入的字符串转换为枚举值。它需要一个枚举类作为参数,如果传入的值不是枚举中的有效成员,它将返回一个错误。curl 请求示例:
2.8 DefaultValuePipe
DefaultValuePipe:用于为没有提供的数据设置默认值。它可以接受一个对象,其中定义了参数名和相应的默认值。curl 请求示例:
2.9 ParseFilePipe
ParseFilePipe:用于处理上传的文件。它通常用于处理 multipart/form-data 请求中的文件上传。⚠️ 注意:
ParseFilePipe 实际上是一个示例名称,NestJS 中用于处理文件上传的管道是 FileInterceptor。curl 请求示例:
上面的内置管道都使用于参数级别,当然还可以用于以下级别:
- 全局应用:所有传入请求都会经过指定的管道。
- 控制器级别:特定控制器中的所有请求会应用到指定的管道。
- 路由处理程序级别:仅应用于某个具体的路由处理函数。
可以看到是非常灵活的,但是一个成熟的项目推荐上全局应用,所有的请求都应该接受管道验证。另外,管道的执行顺序是在路由处理程序执行之前,因此可以确保传入的请求数据在进入业务逻辑之前已经过了验证和转换。
3. ValidationPipe
ValidationPipe 验证管道提供了很多开箱即用的选项,不用我们自己手动去构建验证逻辑。现在,我们在全局应用验证管道。ValidationPipe 需要搭配 class-validator 包中的装饰器来声明验证规则:3.1 class-validator
class-validator 用于验证对象属性的装饰器库,能集成到任何基于类的框架中,如 NestJS。可以验证对象、数组甚至嵌套对象;支持各种验证规则,包括自定义规则;能够与 class-transformer 配合使用,以自动转换和验证数据。3.2 class-transformer
class-transformer 是一个用于转换(序列化和反序列化)和复制对象的库,它可以与 class-validator 无缝集成。它允许将普通的 JavaScript 对象转换为类实例,并在转换过程中进行验证。支持对象的深度复制;可以与 class-validator 结合使用,以在转换过程中进行验证。3.3 全局使用验证管道
简而言之,
class-validator 专注于数据验证,提供了丰富的验证规则和装饰器;class-transformer 专注于数据转换,能够将普通对象转换为类的实例,并支持深度复制。在入口文件中设置全局管道:
3.4 添加验证规则
以 users 资源下的 dto 文件 create-user.dto.ts 为例,添加验证规则:
class-validator 提供的装饰器很好理解:@IsString():验证输入字段是否为字符串类型,如果不是,则验证失败。
@IsNotEmpty():验证字段是否已定义且不是空字符串。
@MinLength(3):确保字段长度至少为 3 个字符,否则验证失败。
@IsEmail():验证字段为一个有效的电子邮箱地址,它使用正则表达式来验证格式。
users.controller.ts 如下:
在调用 this.usersService.create(createUserDto) 方法之前,由于全局使用了验证管道,因此会先对输入参数进行验证。
现在改写 users.service.ts 中创建用户的
create 方法如下:简单来说,会先去检查是否存在指定电子邮件地址的用户,如果存在就返回“邮箱已被注册”的异常提示;否则,使用
bcrypt.hash 方法来创建关于 createUserDto.password 的加密密码,最后返回创建完成的 user 实体数据。(创建的用户 role 为 UserRole.USER)当前的 Prisma 模型文件已经发变化:
User 模型多了
password 用于记录用户密码,role 用于记录用户角色。3.5 测试一下验证管道是否有效
example1:首先输入一个正确的数据格式。

✅ 成功了。
example2:再输入一个错误的格式(email 格式是错误的,密码为空)。

✅ 这是一个很好的预期。
example3:再使用第一个成功创建的用户的邮箱再次创建,看看是否会告诉我们有重复的。

✅ 符合预期,告诉我们邮箱已被注册。
example4:传递不必要的属性给路由处理器(在保证参数有效的前提下,试着多传递一个
title 属性)。
这就直接报出 500 的服务器错误了,虽然但是——不是很优雅。
3.6 删除不必要的属性
ValidationPipe 提供了选项 whitelist: true 来删除 DTO 中未定义的属性。修改 main.ts:
现在再尝试 example5,结果如下:

✅ 正确。
如果想要更加严格地控制输入参数,比如 title 在 dto 文件中并未定义,应该报错。通过添加
forbidNonWhitelisted: true 选项来实现这种效果:
✅ 从结果看,多余的
title 确实被检查出来了,新的 user 也不会被创建出来。4. 转换输入参数的数据类型
在内置管道章节已经列举了很多有关输入参数类型转换的示例,回到项目代码 users.controller.ts 中:
需要传递
id 的路由处理程序在调用服务类方法时都传递了 +id 以使 id 转换为数字类型,这是 JS 提供的自动转换特性,现在可以使用管道(ParseIntPipe)来实现自动转换:5. 自定义管道
先前在用户注册时,我们只对 email 做了检查,其实还要检查用户名是否唯一,我们用自定义管道来实现这个需求。
5.1 定义管道
在 users 目录下创建 pipes 目录,其下新建管道文件 unique-username.pipe.ts:
自定义的
UniqueUsernamePipe 类实现了 PipeTransform 的接口,必须实现 transform 方法来履行 PipeTransform 的接口契约。transform 方法的参数 value 表示当前路由处理器接收的输入参数。因此,上述代码首先使用 prisma 客户端实例去查找对应名称的用户,如果存在则抛出错误:“用户名已存在”,否则将输入参数返回出去。
5.2 使用管道
在 users.controller.ts 中使用定义好的自定义管道:
curl 请求示例(
John Doe 是 User 表中已经存在的对象):
✅ 管道起作用了!
6. 总结
在本节中,我们探讨了如何在 NestJS 框架中利用管道(Pipes)对传入的请求参数执行验证和转换。通过这种方式,管道不仅增强了数据的安全性,确保了接收到的数据符合预期格式和标准,而且还通过自定义管道提供了高度的灵活性,使得对参数的校验可以根据具体业务需求进行定制。
- 作者:菠萝的蜜
- 链接:https://blog.miaoosi.com/backend/article/nestjs-the-mandatory-course-for-chefs-seven-pipes
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。













