Обзор библиотеки FluentValidation. Часть 3. Сообщения об ошибках

от автора

Переопределение сообщений

Вы можете переопределять сообщение об ошибке по умолчанию у конкретного валидатора, с помощью метода расширения 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");   } }
Как свойство ErrorMessage, так и свойство PropertyName содержат название свойства SomeNewName

Как свойство ErrorMessage, так и свойство PropertyName содержат название свойства 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' должно быть заполнено. }

Предыдущая часть Следующая часть


ссылка на оригинал статьи https://habr.com/ru/articles/798961/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *