ValidatorConstraintInterface определяет контракт, который обязаны реализовать все пользовательские валидаторы в библиотеке Class-validator. Этот интерфейс лежит в основе механизма расширяемой валидации и позволяет описывать собственные правила проверки данных, интегрируемые в систему декораторов.
Вся система Class-validator строится вокруг декларативного описания правил валидации через декораторы. Однако встроенных ограничений часто недостаточно, особенно в прикладных сценариях, где требуется бизнес-логика. Для этого используются пользовательские валидаторы, которые реализуют строгий интерфейс.
ValidatorConstraintInterface выступает формальным контрактом между библиотекой и пользовательской реализацией. Любой класс, объявленный как constraint, должен соответствовать этому интерфейсу, иначе он не сможет участвовать в процессе валидации.
Интерфейс задаёт минимальный набор методов, необходимых для работы валидатора:
Каждый метод имеет строго определённую роль в процессе проверки данных.
Метод validate является центральной частью любого пользовательского constraint. Именно он содержит логику проверки значения.
validate(value: any, args?: ValidationArguments): boolean | Promise<boolean>
Метод должен вернуть:
true — если значение соответствует условиям
валидатораfalse — если значение не проходит проверкуPromise<boolean> — если проверка асинхронная
(например, обращение к базе данных или API)value — значение, которое проходит валидациюargs — объект ValidationArguments, содержащий
дополнительный контекстЭтот объект предоставляет метаинформацию о текущей проверке:
value — проверяемое значениеconstraints — массив параметров, переданных в
декораторtargetName — имя класса, к которому относится
проверкаobject — экземпляр объекта, содержащего полеproperty — имя свойства, к которому применён
валидаторИспользование этих данных позволяет создавать контекстно-зависимую валидацию.
Метод defaultMessage используется для генерации сообщения об ошибке, если validate возвращает false.
defaultMessage?(args?: ValidationArguments): string
Реализация интерфейса ValidatorConstraintInterface обычно сопровождается декоратором:
@ValidatorConstraint({ name: 'customName', async: false })
Этот декоратор регистрирует класс как валидатор внутри системы Class-validator и связывает его с механизмом Dependency Injection (если используется соответствующая конфигурация).
Параметры декоратора:
name — уникальное имя constraintasync — флаг, указывающий, используется ли асинхронная
валидацияimport {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments
} from 'class-validator';
@ValidatorConstraint({ name: 'isEven', async: false })
class IsEvenConstraint implements ValidatorConstraintInterface {
validate(value: number, args: ValidationArguments): boolean {
return typeof value === 'number' && value % 2 === 0;
}
defaultMessage(args: ValidationArguments): string {
return `Значение ${args.value} не является чётным числом`;
}
}
После создания constraint он применяется через пользовательский декоратор:
import { Validate } from 'class-validator';
class SampleDto {
@Validate(IsEvenConstraint)
numberValue: number;
}
Декоратор Validate связывает поле с конкретной
реализацией ValidatorConstraintInterface.
При необходимости проверки внешних данных используется асинхронная реализация:
@ValidatorConstraint({ name: 'isUnique', async: true })
class IsUniqueConstraint implements ValidatorConstraintInterface {
async validate(value: string): Promise<boolean> {
const exists = await database.findUser(value);
return !exists;
}
defaultMessage(): string {
return 'Значение должно быть уникальным';
}
}
Асинхронный режим требует установки async: true, иначе
библиотека будет интерпретировать результат как синхронный boolean.
ValidatorConstraintInterface предполагает отсутствие состояния в валидаторе, однако технически класс может содержать поля. Это допустимо, но может привести к проблемам при повторном использовании экземпляров.
Основные ограничения:
При использовании NestJS или других DI-контейнеров валидаторы могут быть зарегистрированы как провайдеры. В этом случае ValidatorConstraintInterface становится частью управляемого жизненного цикла объекта.
Это позволяет:
TypeScript-реализация интерфейса обеспечивает строгую проверку соответствия:
Это делает ValidatorConstraintInterface ключевым элементом типобезопасности в пользовательских расширениях Class-validator.
Если внутри validate возникает исключение, Class-validator
интерпретирует это как неуспешную валидацию. Однако рекомендуется
избегать исключений как механизма управления логикой и всегда возвращать
boolean или Promise
ValidatorConstraintInterface позволяет реализовывать:
Пример кросс-полевой проверки:
validate(value: any, args: ValidationArguments): boolean {
const object = args.object as any;
return value > object.minValue;
}
Class-validator использует Reflect Metadata для хранения информации о декораторах. Реализация ValidatorConstraintInterface регистрируется в этой системе, что позволяет библиотеке:
Типичный цикл работы валидатора:
При проектировании валидаторов на основе ValidatorConstraintInterface учитываются следующие аспекты:
Эти факторы напрямую влияют на масштабируемость системы валидации в крупных приложениях.