Библиотека class-validator возвращает результаты
проверки в виде массива объектов ValidationError. Каждый
такой объект содержит информацию о нарушении правил валидации и может
включать вложенные ошибки при работе с вложенными объектами.
Базовая структура ValidationError включает следующие
ключевые поля:
Пример структуры:
ValidationError {
property: "email",
value: "not-an-email",
constraints: {
isEmail: "email must be an email"
},
children: [],
target: UserDto { email: "not-an-email" }
}
Ключевым элементом обработки ошибок является поле constraints. Оно представляет собой словарь, где:
isEmail,
minLength)Пример:
constraints: {
isEmail: "email must be an email",
minLength: "password is too short"
}
При множественных нарушениях одно свойство может содержать несколько
записей в constraints. Порядок ключей не гарантируется,
поэтому логика обработки не должна опираться на последовательность.
Поле children используется при вложенной валидации объектов и массивов. В таких случаях структура ошибок становится деревом.
Пример DTO:
class Profile {
@IsString()
bio: string;
}
class User {
@ValidateNested()
profile: Profile;
}
Результирующая ошибка:
[
{
property: "profile",
children: [
{
property: "bio",
constraints: {
isString: "bio must be a string"
},
children: []
}
]
}
]
Для корректной интерпретации таких структур требуется рекурсивный обход дерева ошибок.
Для приведения вложенной структуры к плоскому виду применяется
рекурсивная обработка children.
Базовый алгоритм:
function flattenErrors(errors, parentPath = "") {
return errors.flatMap(error => {
const path = parentPath
? `${parentPath}.${error.property}`
: error.property;
const current = error.constraints
? [{
property: path,
messages: Object.values(error.constraints)
}]
: [];
const nested = error.children?.length
? flattenErrors(error.children, path)
: [];
return [...current, ...nested];
});
}
Результатом становится список ошибок с полными путями свойств:
profile.bio → bio must be a string
user.address.city → city should not be empty
Поле constraints часто используется как источник
сообщений для API-ответов или логирования. Типичная обработка включает
преобразование словаря в массив строк:
function extractMessages(error) {
if (!error.constraints) return [];
return Object.values(error.constraints);
}
При наличии нескольких правил для одного свойства формируется массив сообщений, который затем может быть объединён или передан отдельно.
Поле contexts появляется при использовании кастомных
декораторов через ValidatorConstraint.
Пример:
@ValidatorConstraint({ name: "isEven" })
class IsEvenConstraint {
validate(value) {
return value % 2 === 0;
}
defaultMessage() {
return "value must be even";
}
}
При расширенной настройке можно передавать контекст:
contexts: {
isEven: {
expected: "even number",
received: 3
}
}
Интерпретация contexts позволяет:
Разделение важно при отладке сложных DTO, особенно при глубокой вложенности.
target: UserDto { email: "...", profile: {...} }
value: "..."
Часто используется преобразование дерева ошибок в единый формат:
function buildErrorResponse(errors) {
return errors.map(err => ({
field: err.property,
errors: err.constraints
? Object.values(err.constraints)
: []
}));
}
Для серверного анализа сохраняется полная структура:
console.log(JSON.stringify(errors, null, 2));
Это позволяет сохранить вложенность и контекст без потери информации.
Часто применяется формат:
{
"email": ["must be an email"],
"profile.bio": ["must be a string"]
}
Для этого используется объединение property и
рекурсивный обход children.
При применении нескольких валидаторов к одному полю:
@IsEmail()
@Length(10, 50)
email: string;
constraints содержит несколько записей одновременно:
constraints: {
isEmail: "must be an email",
length: "length must be between 10 and 50"
}
Обработка должна учитывать множественность ошибок без раннего прерывания анализа.
Иногда объект ValidationError содержит пустые
constraints при наличии вложенных ошибок в
children. Это характерно для случаев:
@ValidateNested() без собственных правилВ таких случаях анализ выполняется только через
children.
Структура ошибок представляет собой дерево произвольной глубины. Это определяет необходимость:
Игнорирование вложенности приводит к потере части информации при сериализации и формировании ответов.