FluentValidation — это мощная библиотека для валидации объектов в .NET, которая поддерживает создание кастомных сообщений об ошибках. В этом руководстве мы рассмотрим, как использовать различные подходы к формированию этих сообщений и почему важно различать использование простых строк и лямбда-выражений в методе WithMessage
.
Простой вывод сообщения об ошибке
Когда вы используете метод WithMessage
и передаете строку напрямую:
RuleFor(customer => customer.FirstName)
.NotNull()
.WithMessage("Это сообщение об ошибке.");
В данном случае сообщение выводится как есть, но оно не позволяет динамически управлять содержимым. Если вам необходимо добавить динамически изменяемые или кастомные сообщения, стоит рассмотреть использование лямбда-выражений.
Динамическое формирование сообщения с помощью лямбда-выражения
Используя перегрузку метода WithMessage
, вы можете передать лямбда-выражение, которое будет возвращать строку:
RuleFor(customer => customer.FirstName)
.NotNull()
.WithMessage(customer => $"Ошибка: {customer.FirstName} не может быть пустой.");
Этот способ позволяет вам динамически использовать значения, которые могут меняться в зависимости от состояния объекта в процессе валидации. Также вы можете использовать ссылки на другие свойства проверяемого объекта.
Использование пользовательских аргументов
Согласно документации FluentValidation (очень не очевидно там, конечно, это написано), вы можете использовать собственные аргументы в сообщении о проверке. Это могут быть как статические значения, так и ссылки на другие свойства проверяемого объекта.
Пример из документации:
RuleFor(customer => customer.Фамилия)
.NotNull()
.WithMessage(customer => string.Format("Это сообщение ссылается на некоторые константные значения: {0} {1}", "привет", 5));
// Результат будет таким: "Это сообщение ссылается на некоторые константные значения: привет 5"
Но это так же будет работать, если вы будете собирать сообщение об ошибке в Must
или MustAsync
private List<string> ValidationErrors = new List<string>();
public ExampleValidator()
{
RuleFor(x => x)
.MustAsync(async (x, cancellation) =>
{
ValidationErrors.Add("Ошибка1");
return false;
})
.WithMessage(x => string.Join(", ", ValidationErrors));
}
public ExampleValidator()
{
var validationErrors = new List<string>();
RuleFor(x => x)
.Must((x, cancellation) =>
{
ValidationErrors.Add("Ошибка1");
return false;
})
.WithMessage(x => string.Join(", ", validationErrors));
}
Как вы видите, локальная или глобальная переменная – значения не имеет.
Резюмируем про переменные:
Ошибку вам не подсветит, но когда будете выполнять, сломается, потому что пустое сообщение.
var str = "";
...
.Must(x =>
str += "Ошибка";
return false;
)
.WithMessage(str);
// Ничего не вернет, ошибка
Даже если что-то добавите, метод возьмет то, что было изначально.
var str = "";
...
.WithMessage($"Ошибка: {str}");
// Вернет: "Ошибка: "
var str = "Ошибка: ";
...
.Must(x =>
str += "ошибка";
return false;
)
.WithMessage(str);
// Вернет: "Ошибка: "
Это относится не только к строковым переменным, а любым, что бы вы там не выводили.
А вот так будет работать корректно. Используем лямбда-выражения, если объект вам не нужен в формировании сообщения, можно поставить _
var str = "Ошибка: ";
...
.Must(x =>
str += "ошибка";
return false;
)
.WithMessage(_ => str);
// Вернет: "Ошибка: ошибка"
На этом у меня все. Поправьте меня, если что-то не так. Но это то, с чем я боролась последние несколько дней.