Валидация и ValidationHelper
Валидация объектов работает при помощи класса ValidationHelper.
Валидировать можно экземпляр любого класса, в котором поля имеют *Field декораторы из основного пакета, а также декораторы из class-validator.
Field декораторы поддерживают базовый набор правил валидации из class-validator (например, min и max для IntegerField. Более подробно см. описание Fields декораторов).
Для валидации объекта необходимо вызывать функцию ValidationHelper.validate:
public async createOrder(dto: OrderConstructorDto, context: ContextDto) {
await ValidationHelper.validate(dto);
}Кастомные валидаторы
Также поддерживаются кастомные валидаторы, которые можно добавить с помощью декоратора Validator. Данный декоратор может принимать анонимные функции:
@IntegerField({
label: 'ID предложения о доставке',
nullable: true,
})
@Validator((dto: SiteOrderDto) => {
if (dto.shipmentMethod === OrderShipmentMethodEnum.BY_DELIVERY && !dto.deliveryOfferId) {
throw new FieldValidatorException(
`Не может быть пустым для типа доставки "${dto.shipmentMethod}"`,
);
}
})
deliveryOfferId: number;Такая функция принимает 2 аргумента - проверяемый сейчас объект и объект params, реализующий интерфейс IValidatorParams.
Также декоратор Validator может принимать экземпляр сервиса, который имплементирует интерфейс IValidator:
export class StoreExternalIdValidator implements IValidator {
constructor(
private readonly storeRepository: IStoreRepository,
) {}
async validate(dto: StoreSaveDto, params?: IValidatorParams) {
if (!dto.externalId) {
return;
}
const existingStore = await this.storeRepository.createQuery()
.where({
externalId: dto.externalId,
})
.andWhere(['not =', 'id', dto.id])
.one();
if (existingStore) {
throw new FieldValidatorException('Магазин с таким ID уже зарегистрирован');
}
}
}В таком случае в сервис валидации можно подключить внешние сервисы и достать нужные для валидации данные.
Полный пример использования кастомного класса валидации выглядит так:
Объявляем класс, имплементирующий интерфейс
IValidator:tsexport class SomeValidator implements IValidator { constructor() {} async validate(dto: SomeDtoClass, params?: IValidatorParams) { throw new FieldValidatorException('Some error!') } }Передаем этот класс в декоратор
Validatorнад нужным полем класса:tsexport class SomeDtoClass { @IntegerField() @Validator(SomeValidator) id: number; }Подключаем класс валидации в модуль проекта и передаем его в качестве зависимости в сервис, где планируется валидировать объект:
ts@Module({ tables: ModuleHelper.importDir(join(__dirname, '/tables')), permissions, module: () => ({ imports: [], providers: [ // Подключаем валидатор ModuleHelper.provide(SomeValidator, []), // Передаем его в сервис в виде массива валидоров ModuleHelper.provide(SomeService, [ [SomeValidator], ]), ], exports: [], }), }) export class SomeModule {}В сервисе вызываем
ValidationHelper.validate, передав в качестве третьего аргумента массив валидаторов, полученных в конструктореtsexport class SomeService { constructor( private readonly validators: IValidator[], ) {} public async handle(dto: SomeDtoClass, context: ContextDto) { await ValidationHelper.validate(dto, {context}, this.validators); } }
Вызов функции ValidationHelper.validate выбросит исключение ValidationException в случае, если валидация не пройдена. ValidationException содержит объект ошибок типа IErrorsCompositeObject.
Также можно вызвать функции *getClassValidatorErrors и getSteroidsErrors.*
Данные функции не выбросят исключение, а вернут объект указанного выше типа с ошибками из class-validator и кастомных валидаторов соответственно.