
← Предыдущая часть | Следующая часть →
Переопределение сообщений
Вы можете переопределять сообщение об ошибке по умолчанию у конкретного валидатора, с помощью метода расширения WithMessage:
// Модель клиента public class Customer { // Фамилия public string? Surname { get; set; } } // Валидатор для модели клиента public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { // Через вызов метода WithMessage переопределяем сообщение об ошибке у // валидатора NotNull и NotEqual RuleFor(customer => customer.Surname) .NotNull() .WithMessage("Переопределённое сообщение для валидатора NotNull") .NotEqual("Фамилия") .WithMessage("Переопределённое сообщение для валидатора NotEqual"); } }
Обратите внимание, что я выделил табуляцией вызов метода расширения WithMessage, чтобы подчеркнуть то, что он меняет сообщение предыдущего валидатора, не затрагивая другие.
Выполняем:
static void Main(string[] args) { var customer = new Customer { Surname = null }; var validator = new CustomerValidator(); var result = validator.Validate(customer); Console.WriteLine(result.ToString(Environment.NewLine)); // Выведет > "Переопределённое сообщение для валидатора NotNull" // Меняем данные объекта customer, для получения сообщения об ошибке // от валидатора NotEqual customer.Surname = "Фамилия"; result = validator.Validate(customer); Console.WriteLine(result.ToString(Environment.NewLine)); // Выведет > "Переопределённое сообщение для валидатора NotEqual" }
У метода расширения WithMessage есть перегрузка на получение в параметрах валидируемого объекта (не валидируемого свойства, а именно объекта, который валидируется), для вставки его данных в своё кастомное сообщение:
// Модель клиента public class Customer { public string? Surname { get; set; } public decimal Discount { get; set; } } // Валидатор для модели клиента public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { // В методе WithMessage получаем доступ ко всем данным валидируемого объекта RuleFor(customer => customer.Surname) .NotNull() .WithMessage(customer => $"Ссылаемся на значения других свойств: Фамилия {customer.Surname}, Скидка {customer.Discount}"); } }
Всего у метода расширения WithMessage есть 3 перегрузки:
// Можно указать простое сообщение об ошибке IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, string errorMessage) // Можно получить доступ ко всем данным валидируемого объекта IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<T, string> messageProvider) // Можно получить доступ ко всем данным валидируемого объекта, доступ к валидируемому свойству IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<T, TProperty, string> messageProvider)
Заполнители (placeholders)
В каждом валидаторе (в примере выше это NotNull и NotEqual) присутствует свой набор плейсхолдеров (placeholder — заполнитель), с помощью которых можно опционально дополнять свои кастомные сообщения какими-то предопределёнными данными в рантайме (runtime - во время выполнения).
Все встроенные валидаторы (из коробки) содержат следующие заполнители:
{PropertyName} — название валидируемого свойства
{PropertyValue} — значение валидируемого свойства
{PropertyPath} — полный путь к свойству
Пример использования:
// Модель клиента public class Customer { // Адрес public Address? Address { get; set; } } // Модель адреса public class Address { // Фактический адрес public string? Actual { get; set; } } // Валидатор для модели клиента public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(customer => customer.Address.Actual) .Null() .WithMessage("Название свойства: {PropertyName}\n" + "Значение свойства: {PropertyValue}\n" + "Путь к свойству: {PropertyPath}"); } } static void Main(string[] args) { var customer = new Customer { Address = new() { Actual = "Фактический адрес" } }; var validator = new CustomerValidator(); var result = validator.Validate(customer); Console.WriteLine(result.ToString(Environment.NewLine)); // Выведет > // Название свойства: Address Actual // Значение свойства: Фактический адрес // Путь к свойству: Address.Actual }
Конкретный валидатор может включать свои заполнители. Также вы сами можете создавать свои заполнители, но об этом будет в следующих частях.
Переопределение названия свойства в сообщении
По умолчанию встроенные сообщения об ошибках содержат название свойства, которое валидируется:
RuleFor(customer => customer.Surname) .NotNull(); // Выведет "'Surname' должно быть заполнено."
Хоть вы и можете переопределить сообщение целиком через WithMessage, есть возможность переопределить только название свойства с помощью метода WithName:
RuleFor(customer => customer.Surname) .NotNull() .WithName("SomeNewName"); // Выведет "'SomeNewName' должно быть заполнено."
Важно! Изменяется только название свойства в сообщении ErrorMessage, но не у свойства PropertyName у элемента коллекции Errors в ValidationResult, там свойство PropertyName всё также будет содержать значение Surname:

Также есть перегрузка для метода WithName:
// Позволяет получить валидируемый объект и использовать его при генерации названия свойства IRuleBuilderOptions<T, TProperty> WithName<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Func<T, string> nameProvider) // Краткий пример использования перегрузки RuleFor(customer => customer.Surname) .NotNull() .WithName(customer => $"{nameof(customer.Surname)}Foo");
Если вы хотите полностью переопределить название свойства как в ErrorMessage, так и в PropertyName у элемента коллекции Errors, нужно использовать метод OverridePropertyName, вместо метода WithName:
// Валидатор для модели клиента public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { // Полностью переопределяем название свойства в сообщении об ошибке RuleFor(customer => customer.Surname) .NotNull() .OverridePropertyName("SomeNewName"); } }

Как и у метода WithName, у метода OverridePropertyName присутствует такая же перегрузка:
// Позволяет получить валидируемый объект и использовать его при генерации названия свойства IRuleBuilderOptions<T, TProperty> OverridePropertyName<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, Expression<Func<T, object>> expr) // Краткий пример использования перегрузки RuleFor(customer => customer.Surname) .NotNull() .OverridePropertyName(customer => $"{nameof(customer.Surname)}Foo");
По умолчанию названия свойств извлекаются из MemberExpression, который был передан в RuleFor. Если вы хотите изменить это поведение, вы можете задать новое значение для свойства DisplayNameResolver в статическом классе ValidatorOptions.
// Модель клиента public class Customer { public string? Surname { get; set; } } // Валидатор для модели клиента public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(customer => customer.Surname) .NotNull(); } } static void Main(string[] args) { // Меняем поведение по определению названий свойств ValidatorOptions.Global.DisplayNameResolver = (type, member, expression) => { if (member is not null) return member.Name + "Foo"; return null; }; // Дальше валидируем как обычно var customer = new Customer { Surname = null }; var validator = new CustomerValidator(); var result = validator.Validate(customer); Console.WriteLine(result.ToString(Environment.NewLine)); // Выведет > 'SurnameFoo' должно быть заполнено. }
