发布于 2026-01-06 0 阅读
0

NestJS DEV 的全球展示挑战赛(由 Mux 呈现):展示你的项目!

NestJS 中与数据库进行自定义验证

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

NestJS 是一个出色的 Web 框架,它原生支持 TypeScript。其维护者和社区提供了详尽的文档,引导我们逐步了解框架最重要的部分。

但是,当你开始为严肃的应用编写应用程序时,你会很快发现,它无法涵盖一些非常典型的情况(至少对我来说是这样)。

框架提供了多种验证请求数据的方法。我们可以使用管道(Pipes )、基于模式的验证(使用joi 库)或通过集成类验证器ValidatorPipe。最后一种方法是我最喜欢的。为什么呢?主要原因是您可以将所有验证定义放在控制器之外。这是一种很好的分离不同关注点的方法。

Class-Validator 库是一个强大的工具,它提供了一整套差异化验证装饰器,例如`@validate` @Length(10, 20)@IsInt()`@ @Contains('example')expression` 等。我不会介绍如何在 NestJS 中使用基本验证,文档对此解释得非常清楚。

但如果您想创建自己的验证器,并将其与 class-validator 库一起使用呢?很简单,只需快速浏览一下文档,您就可以编写自己的规则并将其与@Validate()装饰器一起使用。更棒的是!编写自己的装饰器并使其包含整个请求验证类非常简单。

当我们需要检查持久存储(例如数据库)中的某些内容时,问题就出现了。简而言之,我们必须注入一些负责与数据库交互的依赖项。例如,某个依赖项UserRepository显然负责用户实体。

幸运的是,class-validator它提供了一个非常方便的useContainer功能,允许设置库要使用的class-validor容器。

所以,请将以下代码添加到您的 main.ts 文件中(app变量是您的 Nest 应用程序实例):

useContainer(app.select(AppModule), { fallbackOnErrors: true });
Enter fullscreen mode Exit fullscreen mode

它允许class-validator使用 NestJS 依赖注入容器。

然后我们可以创建一个存储库,该存储库将查询我们的数据库:

@Injectable()
class UserRepository {
  async getOneOrFail(userId: number): Promise<UserEntity> {
    // some code which fetch user entity or throw exception
  }
}
Enter fullscreen mode Exit fullscreen mode

好的,我们来编写一个Validator Constraint用于存放我们自己的验证逻辑的类。正如你所看到的,我们的依赖项只是简单地注入到类构造函数中:

@ValidatorConstraint({ name: 'UserExists', async: true })
@Injectable()
export class UserExistsRule implements ValidatorConstraintInterface {
  constructor(private usersRepository: UsersRepository) {}

  async validate(value: number) {
    try {
      await this.usersRepository.getOneOrFail(value);
    } catch (e) {
      return false;
    }

    return true;
  }

  defaultMessage(args: ValidationArguments) {
    return `User doesn't exist`;
  }
}
Enter fullscreen mode Exit fullscreen mode

别忘了在相应的模块中将可注入类声明为提供者。

现在您可以使用自定义验证约束了。只需使用@Validate(UserExistsRule)装饰器修饰类属性即可:

export class User {
  @IsInt()
  @Validate(UserExistsRule);
  readonly id: number;
}
Enter fullscreen mode Exit fullscreen mode

如果数据库中不存在该用户,您应该会收到默认错误信息“用户不存在”。虽然使用默认的验证器@Validate()已经足够好用,但您也可以编写自己的装饰器,这样会更方便。编写验证器约束非常快捷方便,我们只需要编写一个带有registerDecorator()函数的装饰器工厂即可。

export function UserExists(validationOptions?: ValidationOptions) {
  return function (object: any, propertyName: string) {
    registerDecorator({
      name: 'UserExists',
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: UserExistsRule,
    });
  };
}
Enter fullscreen mode Exit fullscreen mode

如您所见,您可以编写新的验证器逻辑,或者使用之前编写的验证器约束(在我们的例子中是 UserExistsRule类)。

现在我们可以回到我们的User类中,使用@UserExists验证器而不是@Validate(UserExistsRule)装饰器。

export class User {
  @IsInt()
  @UserExists();
  readonly id: number;
}
Enter fullscreen mode Exit fullscreen mode

希望这篇短文能帮助您在使用 NestJS 框架进行应用开发时应对许多常见场景。我几乎每天都会用到这种方法!

文章来源:https://dev.to/avantar/custom-validation-with-database-in-nestjs-gao