Валидационные ошибки в прикладных системах редко остаются внутри
серверной логики — почти всегда они доходят до пользователя или до слоя
интеграции. Именно поэтому стандартные сообщения, предоставляемые
библиотекой по умолчанию, часто оказываются недостаточными: они либо
слишком общие, либо не соответствуют бизнес-языку предметной области. В
таких случаях используется механизм переопределения сообщений через
параметр message.
messageВ библиотеке class-validator каждая валидирующая
декорация поддерживает опциональный параметр message. Он
позволяет задать собственный текст ошибки, который будет возвращён
вместо стандартного.
Общий принцип:
message не указан — используется системное
сообщение;message указан — он полностью заменяет стандартный
текст;message может быть строкой или функцией.Простейшая форма:
import { IsString } from "class-validator";
class User {
@IsString({ message: "Имя должно быть строкой" })
name: string;
}
В этом случае при нарушении правила IsString
пользователь получит сообщение:
Имя должно быть строкой
Строковый вариант является наиболее простым и часто используемым. Он подходит для фиксированных текстов, не зависящих от входных данных.
Пример использования:
import { IsEmail } from "class-validator";
class Account {
@IsEmail({}, { message: "Некорректный формат email" })
email: string;
}
Важно учитывать, что строка:
Это делает её подходящей для простых правил, но ограничивает гибкость.
Более мощный вариант — передача функции в message. Такая
функция получает объект ValidationArguments, который
позволяет формировать динамические сообщения.
Пример:
import { MinLength } from "class-validator";
class User {
@MinLength(8, {
message: (args) => {
return `Поле ${args.property} должно содержать минимум ${args.constraints[0]} символов`;
},
})
password: string;
}
Здесь используются ключевые элементы:
args.property — имя поля;args.constraints — массив параметров декоратора;args.value — текущее значение поля.Результат при ошибке:
Поле password должно содержать минимум 8 символов
Функциональный message опирается на структуру
аргументов:
value — текущее значение поля;constraints — параметры, переданные в декоратор;targetName — имя класса;property — имя свойства;object — экземпляр проверяемого объекта.Пример использования всех полей:
import { Max } from "class-validator";
class Product {
@Max(1000, {
message: (args) => {
return `Значение поля ${args.property} (${args.value}) превышает допустимый максимум ${args.constraints[0]}`;
},
})
price: number;
}
При ошибке:
Значение поля price (1500) превышает допустимый максимум 1000
В сложных системах сообщения часто стандартизируются. Вместо хаотичного текста создаются шаблоны, отражающие бизнес-терминологию.
Пример:
const REQUIRED_MESSAGE = (field: string) =>
`Поле ${field} обязательно для заполнения`;
class Profile {
@IsString({ message: REQUIRED_MESSAGE("firstName") })
firstName: string;
@IsString({ message: REQUIRED_MESSAGE("lastName") })
lastName: string;
}
Такой подход обеспечивает:
Одно из распространённых применений — мультиязычность. Параметр
message становится точкой интеграции с системой
переводов.
Пример с условной функцией перевода:
function t(key: string): string {
const dictionary = {
"validation.email": "Некорректный email",
"validation.required": "Поле обязательно",
};
return dictionary[key];
}
class Login {
@IsEmail({}, { message: () => t("validation.email") })
email: string;
}
Здесь:
Функция message позволяет реализовать условную
логику.
Пример с проверкой диапазона:
import { Min, Max } from "class-validator";
class Sensor {
@Min(10, {
message: (args) => {
if (args.value < 0) {
return "Значение не может быть отрицательным";
}
return `Минимальное допустимое значение: ${args.constraints[0]}`;
},
})
temperature: number;
}
Такой подход позволяет:
При масштабировании системы появляется необходимость отделять логику сообщений от декораторов.
Пример фабрики сообщений:
function minLengthMessage(field: string, length: number) {
return (args) =>
`Поле ${field} должно быть не короче ${length} символов`;
}
class Register {
@MinLength(6, {
message: minLengthMessage("username", 6),
})
username: string;
}
Преимущества:
При использовании message важно учитывать несколько
особенностей:
message вызывается при каждой ошибке
валидации;Также важно учитывать, что:
message не
поддерживаются;Часто одно поле имеет несколько ограничений:
import { IsString, MinLength, MaxLength } from "class-validator";
class Comment {
@IsString({ message: "Комментарий должен быть строкой" })
@MinLength(10, { message: "Слишком короткий комментарий" })
@MaxLength(200, { message: "Комментарий слишком длинный" })
text: string;
}
Каждое правило формирует собственное сообщение, что позволяет:
При проектировании системы сообщений важно придерживаться нескольких принципов:
Пример различия подходов:
Плохо:
Validation failed
Хорошо:
Поле email должно быть корректным адресом электронной почты
Ещё лучше (контекстно):
Значение "test@" не является допустимым email
В доменно-ориентированных приложениях сообщения часто становятся частью бизнес-логики. Они отражают не только ошибку, но и правила предметной области.
Пример:
class BankAccount {
@Min(0, {
message: (args) =>
`Баланс счета не может быть отрицательным: текущий ${args.value}`,
})
balance: number;
}
Здесь сообщение уже несёт смысловое значение, связанное с доменом, а не с технической проверкой.
При увеличении количества сущностей возникает необходимость централизовать сообщения:
export const ValidationMessages = {
REQUIRED: "Поле обязательно для заполнения",
INVALID_EMAIL: "Некорректный email адрес",
TOO_SHORT: (min: number) => `Минимальная длина: ${min}`,
};
Использование:
@IsEmail({}, { message: ValidationMessages.INVALID_EMAIL })
email: string;
Такой подход упрощает сопровождение и уменьшает вероятность расхождений в тексте.