在NestJS中用Joi实现对象验证的方法

1,089 阅读8分钟

目录

当构建一个强大的可扩展的API时,在你的应用程序中实施适当的验证是很重要的,这不仅是为了安全,也是为了保持你的数据库清洁。如果你正在使用强大的后端API,NestJS可以是一个很好的工具,而且,你可以用NestJS和Joi添加对象验证,使你的应用程序安全。

这篇文章将告诉你如何通过一个基本的CRUD例子在NestJS中实现Joi对象验证。

什么是NestJS?

NestJS深受Angular的启发,是一个 特别流行的框架,用于构建可扩展和强大的应用程序。它提供了一个基于模块、控制器和提供者的简洁架构,以帮助你开始使用。它支持TypeScript,但你也可以用普通的JavaScript编写你的应用程序。

NestJS不会给你强加任何编程范式。你可以自由地使用面向对象、函数式或功能反应式编程。它还在引擎盖下提供了一个优秀的路由机制,并原生支持HTTP服务器框架Express。你也可以配置你的NestJS应用程序来使用Fastify

NestJS的构建块

NestJS有三个主要的构建块:模块、控制器和提供者。

  • 模块:模块用于模块化代码库并将其分割成可重用的组件。被分组的TypeScript文件用@Module 装饰器描述,以提供元数据。NestJS使用这些文件来组织应用程序的结构
  • 控制器:控制器控制传入的请求并向客户端发送适当的响应
  • 提供者:提供者是NestJS的基本概念。服务、工厂、助手、资源库等,在Nest中被视为提供者。提供者可以作为Nest中的一个依赖关系被注入。

为什么使用NestJS?

近年来,Node.js已成为一种流行的编程语言,而且,由于开发人员希望更快地编写可投入生产的API,对高质量的后端框架的需求也在增加。进入NestJS,它可以帮助开发者更快地编写高效的代码。

让我们来讨论一下使用NestJS的好处。

  • TypeScript。NestJS默认使用TypeScript,但你也可以用vanilla JavaScript编写NestJS代码。
  • 微服务。NestJS支持GraphQLWebSocketsgRPCMQTT,以及REST APIs。对这些工具的支持有助于编写微服务。
  • CLI:Nest也有自己的CLI,它使你能够创建、运行、构建你的应用程序,以及更多。
  • 高质量的文档。NestJS有很好的文档,可以帮助你深入理解各种概念。它甚至提供由NestJS团队成员创建的官方课程
  • 测试。NestJS提供了对使用Jest测试你的应用程序的支持,或者你选择的任何其他测试框架。NestJS甚至提供了一个自己的测试包

什么是Joi?

所有的开发者都知道,验证来自客户端的数据是至关重要的。如果你曾经在Node.js中使用过MongoDB和mongoose,你可能对mongoose模式很熟悉。Mongoose模式有助于描述数据,并轻松为数据添加验证器。Joi与schemas非常相似。

Joi是一个广泛使用的Node.js数据验证库,提供了一个简单、直观、可读的API来描述数据。它主要用于验证从API端点发送的数据,并允许你创建你想接受的数据类型的蓝图。

下面是一个用Joi描述模式的简单例子。

 const schema = Joi.object().keys({ 
     name: Joi.string().alphanum().min(3).max(30).required(),
     birthyear: Joi.number().integer().min(1970).max(2013), 
});

用NestJS构建一个基本的API

NestJS提供了许多选项,可以根据你的需求来构建代码,例如CRUD配方。这允许你在几秒钟内从Nest CLI中用端点搭建一个CRUD的支架。

要在你的电脑上安装Nest CLI,运行以下命令。

npm i -g @nestjs/cli  

下一步是生成一个Nest应用程序。Nest CLI使用以下命令。

nest new project-name

这里,project-name 是项目的名称。命令完成后,运行下面的命令,将CRUD端点支架化。

nest g resource users

它会问你几个问题,比如要使用哪个传输层。一旦你根据你的偏好选择了这些选项,Nest将为CRUD API提供支架。例如,一个带有users 端点的API将由上述命令生成。你可以看到新的users 文件夹。

现在,如果你用npm run start:dev 运行应用程序,你会看到控制台中记录的端点。你的服务器将在端口3000 启动。

你可以通过访问端点或打开users.controllers.ts 文件来检查端点。这个文件包含CRUD API的路由。每个API的服务都在users.service.ts 文件中定义,所有这些文件都在users 文件夹下。

对象验证的重要性

users.controllers.ts 如果你看一下GET 文件中寻找单一项目的方法,你会发现没有设置验证。你可以使用任何东西作为ID,而Nest不会抛出验证错误。

OWASP的十大安全风险列表中提到,注入攻击仍然是最流行的安全风险之一。OWASP还提到,当 "用户提供的数据没有经过应用程序的验证、过滤或消毒 "时,应用程序就容易受到注入攻击。

这清楚地表明,数据验证是一个重要的安全问题,在构建应用程序时要牢记。有一些内置的管道,可以验证或修改输入。NestJS有八个内置管道。如果你想让ID只能是整数类型的,你可以使用ParseIntPipe 管道。这里有一个例子。

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: string) {
  return this.usersService.findOne(+id);
}

如果你试图用数字以外的任何ID打到端点,你会收到以下错误。

Built-in Pipes Validation with NextJS

使用一个内置的管道很简单,但是将其用于一个大型的模式是很复杂的。Joi使得在NestJS中设计模式和实现验证变得更容易。让我们为NestJS项目实现Joi。

在NestJS中实现Joi

一般来说,任何可以注入NestJS的脚本都是Pipe 类。管道主要有两个使用情况。

  • 转换输入数据
  • 验证输入数据

你可以在官方文档中阅读更多关于管道的内容。

第一步是安装必要的包。这里,只有Joi包是必需的。运行下面的命令来安装该包。

npm i joi

现在,在users 目录内创建一个名为validation.pipe.ts 的新文件。创建一个自定义管道来实现验证是非常直接的。这里有一个代码片段来帮助你理解。

import {
  PipeTransform,
  BadRequestException,
    ArgumentMetadata
} from '@nestjs/common';

export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

任何传入这个管道构造函数的模式都将被检查为配置的Joi验证模式。为了使上述验证器工作,打开dto 文件夹内的create-user.dto.ts 文件。

在这里,定义一个API在保存数据时将使用的模式类型。为了简单起见,假设用户发送的模式和数据库持有的模式具有相同的结构。

我们假设API将firstname,lastname,email,isVerified, 和phoneNumber 作为输入。DTO将看起来像这样。

export class CreateUserDto {
  public firstname: string;
  public lastname: string;
  public isVerified: boolean;
  public email: string;
  public phoneNumber: number;
}

现在,在user.dto.js 文件内定义Joi模式。你也可以使用单独的文件来存储模式。在这个例子中,Joi的用户模式很简单。

import Joi from 'joi';

export const UserSchema = Joi.object({
  firstname: Joi.string().required(),
  lastname: Joi.string().required(),
  email: Joi.string().email().required(),
  isVerified: Joi.boolean().required(),
  phoneNumber: Joi.number(),
}).options({
  abortEarly: false,
});

该模式是非常不言自明的。string() 方法确保输入的类型是string ,而required() 方法确保字段在输入里面。同样地,booleannumber 确保类型是布尔型或数字型。

options 方法在一个对象内接受其他选项。abortEarly 方法,当设置为true 时,当它发现第一个错误时停止验证。否则,它会返回所有的错误。

现在模式已经准备好了,是时候相应地更新验证管道了。
这里是完整的validation.pipe.ts 文件。

import { PipeTransform, BadRequestException } from '@nestjs/common';

import { CreateUserDto } from './dto/create-user.dto';

import { UserSchema } from './dto/user.dto';

export class CreateUserValidatorPipe implements PipeTransform<CreateUserDto> {
  public transform(value: CreateUserDto): CreateUserDto {
    const result = UserSchema.validate(value);
    if (result.error) {
      const errorMessages = result.error.details.map((d) => d.message).join();
      throw new BadRequestException(errorMessages);
    }
    return value;
  }
}

自定义验证器类接受并返回CreateUserDto 类。const result = UserSchema.validate(value); ,根据定义的Joi模式验证结果。如果结果有任何错误,则使用map 方法对结果进行映射。错误信息被连接在一起。最后,错误信息被发送到客户端。否则,它返回输入值。

如果输入值通过了验证,它将根据user.service.ts 文件内定义的方法显示信息,"This action adds a new user ,"。

我们现在已经在NestJS中实施了Joi。你可以通过向端点发送JSON有效载荷来查看验证是否有效 [http://localhost:3000/users](http://localhost:3000/users).

Validation in Joi NextJS

上面的图片显示了有效载荷通过验证时的样子。同样地,下面的图片显示了基于验证的不同错误。

Validation Errors in Joi

总结

本文概述了NestJS和Joi以及验证在我们应用程序中的重要性,然后引导你在NestJS应用程序中实现验证。我希望你觉得它很有用。