
本文详细介绍了如何在nest.js中使用`class-validator`创建自定义异步验证器,并解决在其中注入typeorm仓库时遇到的`null`引用问题。核心在于通过`@injectable()`装饰器将验证器纳入nest.js的依赖注入体系,并在相关模块中正确配置`typeormmodule.forfeature()`和将验证器注册为`provider`,从而确保仓库实例能够被正确注入并用于数据库操作。
在Nest.js应用开发中,数据验证是确保数据完整性和业务逻辑正确性的关键环节。class-validator库与Nest.js的集成提供了强大而灵活的验证能力,允许开发者定义各种复杂的验证规则,包括自定义异步验证。然而,当自定义验证器需要与数据库交互(例如检查某个字段的唯一性)时,正确地注入TypeORM仓库(Repository)常常成为一个挑战。本文将深入探讨如何解决在自定义class-validator约束中TypeORM仓库为null的问题,并提供一套完整的解决方案。
1. 自定义验证器与仓库注入的挑战
考虑一个常见的场景:我们需要验证一个实体(例如Parking)的名称在数据库中是否唯一。为此,我们可以创建一个自定义的class-validator装饰器和约束。最初的尝试可能如下所示:
// src/validators/unique-name.constraint.tsimport { InjectRepository } from '@nestjs/typeorm';import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments,} from 'class-validator';import { Repository } from 'typeorm';import { Parking } from '../entities/parking.entity'; // 假设这是您的实体@ValidatorConstraint({ async: true })export class UniqueNameConstraint implements ValidatorConstraintInterface { constructor( @InjectRepository(Parking) private parkingRepository: Repository, ) {} async validate(name: any, args: ValidationArguments) { // 问题:这里的 this.parkingRepository 总是 null const parking = await this.parkingRepository.findOne({ where: { name } }); return !parking; // 如果未找到,则名称唯一 } defaultMessage(args: ValidationArguments) { return 'The name already exists'; }}export function UniqueName(validationOptions?: ValidationOptions) { return function (object: object, propertyName: string) { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [], validator: UniqueNameConstraint, }); };}
并在DTO中使用它:
// src/dto/create-entity.input.tsimport { Field, InputType } from '@nestjs/graphql'; // 假设使用GraphQLimport { UniqueName } from '../validators/unique-name.constraint';@InputType()export class CreateEntityInput { @UniqueName({ message: 'The name already exists' }) @Field(() => String, { description: 'Name of the entity' }) name: string;}
当运行上述代码时,UniqueNameConstraint中的this.parkingRepository会是null,导致数据库查询失败。这是因为class-validator在实例化UniqueNameConstraint时,并没有通过Nest.js的依赖注入(DI)容器来完成,因此@InjectRepository装饰器无法发挥作用。Nest.js的DI容器负责解析和提供所有被其管理的类的依赖。
2. 解决方案:将自定义验证器纳入Nest.js DI体系
要解决这个问题,我们需要确保UniqueNameConstraint也被Nest.js的DI容器所管理。这需要三个关键步骤:
2.1. 将验证器标记为可注入
使用@Injectable()装饰器标记UniqueNameConstraint类。这告诉Nest.js这个类可以被DI容器管理和实例化。
// src/validators/unique-name.constraint.tsimport { Injectable } from '@nestjs/common'; // 导入 Injectableimport { InjectRepository } from '@nestjs/typeorm';import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments,} from 'class-validator';import { Repository } from 'typeorm';import { Parking } from '../entities/parking.entity';@Injectable() // <--- 添加此行@ValidatorConstraint({ async: true })export class UniqueNameConstraint implements ValidatorConstraintInterface { constructor( @InjectRepository(Parking) private parkingRepository: Repository, ) {} async validate(name: any, args: ValidationArguments) { const parking = await this.parkingRepository.findOne({ where: { name } }); return !parking; } defaultMessage(args: ValidationArguments) { return 'The name already exists'; }}// ... UniqueName 装饰器保持不变 ...
2.2. 在模块中注册实体仓库
为了让@InjectRepository(Parking)能够找到并注入Parking实体的仓库,你需要在相关的Nest.js模块中注册该实体。这通常通过TypeOrmModule.forFeature()方法完成。
假设你的Parking实体在ParkingModule中使用,那么ParkingModule的imports数组应包含:
// src/parking/parking.module.tsimport { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { Parking } from './entities/parking.entity'; // 导入您的实体// ... 其他导入@Module({ imports: [ TypeOrmModule.forFeature([Parking]), // <--- 注册 Parking 实体仓库 // ... 其他模块 ], // ...})export class ParkingModule {}
如果你的自定义验证器在其他模块中使用,或者你的应用结构比较简单,你也可以在AppModule中注册。关键是确保在验证器被实例化之前,Parking实体的仓库已经通过forFeature注册。
2.3. 将自定义验证器注册为Provider
最后,你需要将UniqueNameConstraint类添加到Nest.js模块的providers数组中。这告诉Nest.js,UniqueNameConstraint是一个可被注入的服务,DI容器应该管理它的生命周期并解析其依赖。
// src/parking/parking.module.ts 或其他相关模块import { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { Parking } from './entities/parking.entity';import { UniqueNameConstraint } from '../validators/unique-name.constraint'; // 导入您的验证器@Module({ imports: [ TypeOrmModule.forFeature([Parking]), ], providers: [ UniqueNameConstraint, // <--- 将验证器添加到 providers 数组 // ... 其他服务 ], exports: [ // 如果其他模块需要使用 ParkingModule 提供的服务,可以导出 ]})export class ParkingModule {}
注意事项:
模块选择: 最佳实践是将自定义验证器放在其所验证实体相关的模块中,或者创建一个独立的ValidationModule来集中管理所有自定义验证器。全局验证器: 如果你的自定义验证器需要在整个应用中广泛使用,你可以在AppModule中将其注册为provider。异步验证: class-validator的@ValidatorConstraint({ async: true })和async validate(…)方法是处理异步验证(如数据库查询)的关键。
3. 完整示例与总结
通过以上三个步骤,UniqueNameConstraint现在已经被Nest.js的DI容器管理。当class-validator需要实例化UniqueNameConstraint时,Nest.js将负责提供一个带有正确注入的parkingRepository实例的验证器。
// src/parking/parking.module.ts (示例)import { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { Parking } from './entities/parking.entity';import { UniqueNameConstraint } from '../validators/unique-name.constraint'; // 确保路径正确@Module({ imports: [ TypeOrmModule.forFeature([Parking]), // 注册实体仓库 ], providers: [ UniqueNameConstraint, // 注册自定义验证器为 provider // 其他与停车相关的服务... ], // 如果需要,可以导出 providers exports: [UniqueNameConstraint]})export class ParkingModule {}
现在,你的自定义验证器应该能够正确地访问TypeORM仓库并执行数据库查询,从而实现复杂的异步验证逻辑。
总结:
在Nest.js中创建带有数据库交互的自定义class-validator约束时,核心在于理解Nest.js的依赖注入机制。确保以下三点:
@Injectable(): 将自定义验证器类标记为可注入。TypeOrmModule.forFeature(): 在相关模块中注册实体仓库。providers数组: 将自定义验证器类添加到模块的providers数组中。
遵循这些步骤,你将能够构建出健壮且功能强大的自定义验证逻辑,有效提升Nest.js应用的可靠性。
以上就是正确在Nest.js自定义验证器中注入TypeORM仓库的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1534090.html
微信扫一扫
支付宝扫一扫