1

我正在尝试为 Typestack 类验证器创建一个新的约束。“IsUnique”约束将实体作为类型,并将它的列作为参数来检查该列是否在数据库中不存在并且是唯一的。

我已经尝试了下面的代码,但不知何故我无法通过 registerDecorator 中的验证器键将类型传递给“IsUniqueConstraint” 。因为,我是 Typescript 的新手,所以我不太了解它的概念。

有人可以帮我知道我们该怎么做吗?

is-unique.constraint.ts

import { registerDecorator, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments, ValidationOptions } from 'class-validator';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';

@ValidatorConstraint({ name: 'isUnique', async: true })
@Injectable()
export class IsUniqueConstraint<T> implements ValidatorConstraintInterface {

    constructor(private readonly repository: Repository<T>) { }

    async validate(value: string, args: ValidationArguments) {
        const [column] = args.constraints;

        const result = await this.repository.findOne({ where: { [column]: value } });

        if (result) {
            return false;
        }

        return true;
    }

    defaultMessage(args: ValidationArguments) {
        return `"${args.value}" already exists for ${args.constraints[0]}`;
    }

}

export function IsUnique<T>(column: string, validationOptions?: ValidationOptions) {
    return (object: object, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [column],
            validator: IsUniqueConstraint,
        });
    };
}

用户.dto.ts

import { IsNotEmpty } from 'class-validator';
import { IsUnique } from './../shared/constraints/is-unique.constraint';
import { User } from './user.entity';

export class CreateUserDto {
  @IsNotEmpty()
  @IsUnique<User>('username')
  readonly username: string;
}
4

2 回答 2

1

泛型通常是仅编译时的功能。除非您有某种方式发出元数据,包括泛型(不确定这是否容易实现)。

如果您需要在运行时使用类型,通常应该将其作为常规参数传递,因此在这种情况下,必须更改签名以适应这种情况:

@IsUnique(User, 'username')

这可能就是为什么在注入存储库时你通过 来执行它@InjectRepository(User),它也将实体类作为参数。我怀疑是否IsUniqueConstraint可以按原样注入存储库。您可能需要根据装饰器传递的实体类型从 DI 容器/连接管理器中解决它。

根据文档,您可以直接将对象分配给validator,而不仅仅是类/构造函数,因此您可以创建验证器的具体实例,手动将解析的存储库传递给构造函数。

所以,也许是这样的:

import { getRepository } from "typeorm";
// ...

export function IsUnique(
    entity: Function,
    column: string,
    validationOptions?: ValidationOptions) {

    // Not sure if this works here. Maybe it needs to be
    // moved into the returned function or a different resolution
    // mechanism is required.
    const repository = getRepository(entity); 

    return (object: object, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [column],
            validator: new IsUniqueConstraint(repository),
        });
    };
}
于 2019-10-08T22:09:08.990 回答
1

好的,在尝试了很多之后,我以另一种方式解决了它。感谢@HB 向我展示了路径。

为此,我将实体传递给验证器并在类本身中生成存储库。因为 Nest JS Injection 仅适用于类。

import { registerDecorator, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments, ValidationOptions } from 'class-validator';
import { Injectable } from '@nestjs/common';
import { Connection } from 'typeorm';
import { InjectConnection } from '@nestjs/typeorm';

@ValidatorConstraint({ name: 'isUnique', async: true })
@Injectable()
export class IsUniqueConstraint implements ValidatorConstraintInterface {

    constructor(@InjectConnection() private readonly connection: Connection) { }

    async validate(value: string, args: ValidationArguments) {
        const [entity, column] = args.constraints;

        const repository = this.connection.getRepository(entity);
        const result     = await repository.findOne({ where: { [column]: value } });

        if (result) {
            return false;
        }

        return true;
    }

    defaultMessage(args: ValidationArguments) {
        return `"${args.value}" already exists for ${args.constraints[1]}`;
    }

}

export function IsUnique(entity: Function, column: string, validationOptions?: ValidationOptions) {
    return (object: object, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [entity, column],
            validator: IsUniqueConstraint,
        });
    };
}
于 2019-10-09T15:57:18.230 回答