相关文章推荐
害羞的滑板  ·  学习nest.js的第三天_nestjs dto·  5 天前    · 
慷慨的烤面包  ·  【Nest教程】实现一个简单的用户增删改查功 ...·  5 天前    · 
勤奋的长颈鹿  ·  Nest.js ...·  5 天前    · 
不羁的槟榔  ·  oracle获取某时间段内的年份、月份、日期 ...·  3 月前    · 
骑白马的盒饭  ·  python 等待某个文件处理完毕 ...·  11 月前    · 
气宇轩昂的蜡烛  ·  com.microsoft.sqlserve ...·  2 年前    · 
酒量小的骆驼  ·  Vue Print-js ...·  2 年前    · 
Code  ›  Nest.js 从零到壹系列(五):使用管道、DTO 验证入参,摆脱 if-else 的恐惧开发者社区
pipe string nest dto
https://cloud.tencent.com/developer/article/1610050
勤奋的长颈鹿
5 天前
一只图雀

Nest.js 从零到壹系列(五):使用管道、DTO 验证入参,摆脱 if-else 的恐惧

腾讯云
开发者社区
文档 建议反馈 控制台
首页
学习
活动
专区
圈层
工具
MCP广场
文章/答案/技术大牛
发布
首页
学习
活动
专区
圈层
工具
MCP广场
返回腾讯云官网
一只图雀
首页
学习
活动
专区
圈层
工具
MCP广场
返回腾讯云官网
社区首页 > 专栏 > Nest.js 从零到壹系列(五):使用管道、DTO 验证入参,摆脱 if-else 的恐惧

Nest.js 从零到壹系列(五):使用管道、DTO 验证入参,摆脱 if-else 的恐惧

作者头像
一只图雀
发布 于 2020-04-07 16:01:07
发布 于 2020-04-07 16:01:07
4.4K 0 0
代码可运行
举报
文章被收录于专栏: 图雀社区 图雀社区
运行总次数: 0
代码可运行
本文由图雀社区认证作者 布拉德特皮 写作而成

前言

上一篇介绍了如何使用中间件、拦截器、过滤器打造日志系统,接下来将介绍后端永远绕不过去的痛:参数验证。

你是否曾经为了验证参数,写了一大堆 if - else ?然后还要判断各种参数类型?相似的结构在不同的方法里判断,却又要复制一遍代码?

使用 DTO 可以清晰的了解对象的结构,使用 Pipes(管道)配合 class-validator 还可以对参数类型进行判断,还可以在验证失败的时候抛出错误信息。

前两天发现 NestJS 更新到了 7.0.3(之前是 6.0.0),为了让教程更贴合实际,故果断升级。升级后没发现什么大问题,之前的代码照常运行,若各位读者发现什么其他 Bug ,可以在 GitHub 上 issues。

GitHub 项目地址[1] ,欢迎各位大佬 Star。

一、什么是 DTO?

数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统。数据传输目标往往是数据访问对象从数据库中检索数据。数据传输对象与数据交互对象或数据访问对象之间的差异是一个以不具有任何行为除了存储和检索的数据(访问和存取器)。

根据定义,我们需要在代码中约定一下 DTO,还是以注册接口为例,先创建 user.dto.ts 简单定义一下:

代码语言: javascript
代码 运行次数: 0
运行
复制
// src/logical/user
export class RegisterInfoDTO {
  readonly accountName: string | number;
  readonly realName: string;
  readonly password: string;
  readonly repassword: string;
  readonly mobile: number;

其实就是输出了一个类似于声明接口的 class,表明了参数名和类型,并且是只读的。

当然,Nest 支持使用 Interface(接口) 来定义 DTO,具体语法可以浏览 TypeScript 官方文档,不过 Nest 建议使用 Class 来做 DTO(就踩坑经验而言, Class 确实比 Interface 方便多了),所以 Interface 在这里就不多介绍了。

定义好 DTO 后,接下来将演示怎么和管道配合来验证参数。

二、管道

1. 概念

管道和拦截器有点像,都是在数据传输过程中的“关卡”,只不过各司其职。

管道有两个类型:

  • 转换:管道将输入数据转换为所需的数据输出;
  • 验证:对输入数据进行验证,如果验证成功继续传递,验证失败则抛出异常;

ValidationPipe 是 Nest.js 自带的三个开箱即用的管道之一(另外两个是 ParseIntPipe 和 ParseUUIDPipe ,现在还用不到)。

ValidationPipe 只接受一个值并立即返回相同的值,其行为类似于一个标识函数,标准代码如下:

代码语言: javascript
代码 运行次数: 0
运行
复制
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;

每个管道必须提供 transform() 方法。这个方法有两个参数:

  • value
  • metadata

value 是当前处理的参数,而 metadata 是其元数据。

2. 创建管道

简单介绍完一些概念后,开始实战,先创建 pipe 文件:

代码语言: javascript
代码 运行次数: 0
运行
复制
$ nest g pipe validation pipe

这里我们还需要安装两个依赖包:

代码语言: javascript
代码 运行次数: 0
运行
复制
$ yarn add class-validator class-transformer -S

然后在 validation.pipe.ts 中编写验证逻辑:

代码语言: javascript
代码 运行次数: 0
运行
复制
// src/pipe/validation.pipe.ts
import { ArgumentMetadata, Injectable, PipeTransform, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { Logger } from '../utils/log4js';
@Injectable()
export class ValidationPipe implements PipeTransform {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    console.log(`value:`, value, 'metatype: ', metatype);
    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]; // 只需要取第一个错误信息并返回即可
      Logger.error(`Validation failed: ${msg}`);
      throw new BadRequestException(`Validation failed: ${msg}`);
    return value;
  private toValidate(metatype: any): boolean {
    const types: any[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);

3. 绑定管道

绑定管道非常简单,就和之前使用 Guards 那样,直接用修饰符绑定在 Controller 上,然后将 body 的类型指定 DTO 即可:

代码语言: javascript
代码 运行次数: 0
运行
复制
// src/logical/user/user.controller.ts
import { Controller, Post, Body, UseGuards, UsePipes } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from '../auth/auth.service';
import { UserService } from './user.service';
import { ValidationPipe } from '../../pipe/validation.pipe';
import { RegisterInfoDTO } from './user.dto'; // 引入 DTO
@Controller('user')
export class UserController {
  constructor(private readonly authService: AuthService, private readonly usersService: UserService) {}
  // JWT验证 - Step 1: 用户请求登录
  @Post('login')
  async login(@Body() loginParmas: any) {
  @UseGuards(AuthGuard('jwt'))
  @UsePipes(new ValidationPipe()) // 使用管道验证
  @Post('register')
  async register(@Body() body: RegisterInfoDTO) { // 指定 DTO类型
    return await this.usersService.register(body);

4. 完善错误提示

光有这些还不行,我们应该增加错误提示:

代码语言: javascript
代码 运行次数: 0
运行
复制
// src/logical/user/user.dto.ts
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class RegisterInfoDTO {
  @IsNotEmpty({ message: '用户名不能为空' })
  readonly accountName: string | number;
  @IsNotEmpty({ message: '真实姓名不能为空' })
  @IsString({ message: '真实姓名必须是 String 类型' })
  readonly realName: string;
  @IsNotEmpty({ message: '密码不能为空' })
  readonly password: string;
  @IsNotEmpty({ message: '重复密码不能为空' })
  readonly repassword: string;
  @IsNotEmpty({ message: '手机号不能为空' })
 
推荐文章
害羞的滑板  ·  学习nest.js的第三天_nestjs dto
5 天前
慷慨的烤面包  ·  【Nest教程】实现一个简单的用户增删改查功能 - 青年码农
5 天前
勤奋的长颈鹿  ·  Nest.js 从零到壹系列(五):使用管道、DTO 验证入参,摆脱 if-else 的恐惧开发者社区
5 天前
不羁的槟榔  ·  oracle获取某时间段内的年份、月份、日期列表,两个年份之间的所有年份,日期函数_oracle 年份函数-CSDN博客
3 月前
骑白马的盒饭  ·  python 等待某个文件处理完毕 再进行下一步_mob64ca12e33720的技术博客_51CTO博客
11 月前
气宇轩昂的蜡烛  ·  com.microsoft.sqlserver.jdbc.SQLServerException: 该连接已关闭. 这个异常要怎么解决??_百度知道
2 年前
酒量小的骆驼  ·  Vue Print-js 打印问题记录~-阿里云开发者社区
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号