FluentValidation — це бібліотека валідації для .NET Core Project, яка дозволяє визначати правила для ваших моделей. Вона відокремлює логіку валідації від решти вашого коду і надає високо гнучкий і зрозумілий спосіб забезпечити правильність ваших даних перед їх обробкою.
Анотації даних є переважною і простою у використанні структурою, але клас, який функціонально спроектовано як сутність, також піддається валідації, що суперечить "Принципу єдиної відповідальності" (Single Responsibility Principle), одному з Принципів SOLID, про які я згадував у моїй попередній статті. Тому було б корисніше створити окремий клас для виконання процесу валідації.
API Документація
Для отримання додаткової інформації про FluentValidation та її можливості, перегляньте офіційну Документацію FluentValidation і посилання на GitHub.
Додавання пакетів
Спочатку створіть проект .NET Core. Потім встановіть пакет FluentValidation.AspNetCore
(включає залежності FluentValidation
та FluentValidation.DependencyInjectionExtensions
бібліотеки) через NuGet:
Якщо ви додаєте пакет через Package Manager Console, введіть наступну команду в консолі:
Install-Package FluentValidation.AspNetCore
Якщо ви додаєте пакет через .NET CLI, введіть наступну команду в консолі:
dotnet add package FluentValidation.AspNetCore
Реєстрація Сервісу
Цей ValidationMiddleware
є власною реалізацією фільтра валідації для .NET Core. Він забезпечує, щоб вхідні запити з недійсними станами моделей перехоплювались і оброблялись до досягнення бізнес-логіки.
using FluentValidation;
using Microsoft.AspNetCore.Mvc.Filters;
namespace SM.Core.Common.Middleware
{
public class ValidationMiddleware : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ModelState.IsValid)
{
var errorsInModelState = context.ModelState.Where(p => p.Value.Errors.Count > 0)
.ToDictionary(keyValuePair => keyValuePair.Key, keyValuePair => keyValuePair.Value.Errors
.Select(p => p.ErrorMessage)).ToArray();
var list = errorsInModelState.SelectMany(error => error.Value).ToList();
if (list.Count != 0)
{
var errorLines = list.Select(s => s).Distinct().Aggregate((current, next) => current + "\n" + next);
throw new ValidationException(errorLines);
}
}
await next();
}
}
}
Цей клас BusinessStartup
централізує конфігурацію для FluentValidation та інших налаштувань бізнес-логіки в бізнес-шарі всередині проекту .NET Core.
AddFluentValidationAutoValidation()
: Включає автоматичну валідацію моделей за допомогою FluentValidation. Це оновлений метод, який замінює застарілийAddFluentValidation()
.AddFluentValidationClientsideAdapters()
: Включає адаптери валідації для клієнтської сторони для інтеграції з JavaScript фреймворками або бібліотеками.AddValidatorsFromAssembly(typeof(BusinessStartup).Assembly)
: Реєструє всі класи валідаторів (які реалізуютьIValidator
) з асемблеї, де розташований класBusinessStartup
.
Вона динамічно виявляє та реєструє валідатори, що полегшує керування кількома валідаторами.
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using SM.Core.Common.Middleware;
namespace SM.AuthorizationMgmt.Business
{
public static class BusinessStartup
{
public static IServiceCollection AddBusinessStartup(this IServiceCollection services)
{
//Налаштування FluentValidation
//// Після: Включення лише автованої валідації
services.AddFluentValidationAutoValidation();
//// Після: Включення лише клієнтської валідації:
services.AddFluentValidationClientsideAdapters();
//services.AddValidatorsFromAssemblyContaining(ServiceLifetime.Transient);
services.AddValidatorsFromAssembly(typeof(BusinessStartup).Assembly);
services.AddControllers(option => option.Filters.Add());
//AddFluentValidation() [застаріло]
//services.AddControllers(options =>
//{
// options.Filters.Add();
//}).AddFluentValidation(configuration => configuration.RegisterValidatorsFromAssembly(typeof(BusinessStartup).Assembly));
//[ApiController] тег запобігає за замовчуванням поверненню BadRequest
services.Configure(options => options.SuppressModelStateInvalidFilter = true);
return services;
}
}
}
Опис моделі
Наступний приклад використовуватиме об'єкт RegisterUserCommand.
using MediatR;
using SM.Core.Utilities.Results;
namespace SM.AuthorizationMgmt.Business.Features.Authorizations.Commands
{
public class RegisterUserCommand : IRequest
{
public string Username { get; set; }
public long CitizenId { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string MobilePhone { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
}
Створення класу валідатора
Давайте створимо клас валідатора, який успадковується від AbstractValidator, де T — це наш клас RegisterUserCommand.
Описуйте правила валідації всередині класу валідатора.
using FluentValidation;
using SM.AuthorizationMgmt.Business.Features.Authorizations.Commands;
using SM.Core.Common.Constants;
namespace SM.AuthorizationMgmt.Business.Features.Authorizations.Validations
{
public class RegisterUserValidator : AbstractValidator
{
public RegisterUserValidator()
{
RuleFor(x => x.Username)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Length(2, 100).WithMessage("{PropertyName} " + string.Format(Messages.MustBeBetweenCharacter, "{MinLength}", "{MaxLength}"));
RuleFor(x => x.CitizenId)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Must(Be11Digits).WithMessage("{PropertyName} " + string.Format(Messages.MustBeCharacter, "11"));
RuleFor(x => x.CitizenId)
.Must(ValidateCitizenId).WithMessage("{PropertyName} " + Messages.IsNotValid)
.When(x => Be11Digits(x.CitizenId));
RuleFor(x => x.Name)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Length(2, 100).WithMessage("{PropertyName} " + string.Format(Messages.MustBeBetweenCharacter, "{MinLength}", "{MaxLength}"));
RuleFor(x => x.Surname)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Length(2, 100).WithMessage("{PropertyName} " + string.Format(Messages.MustBeBetweenCharacter, "{MinLength}", "{MaxLength}"));
RuleFor(x => x.Email)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.EmailAddress().WithMessage("{PropertyName} " + Messages.IsNotValid);
RuleFor(x => x.MobilePhone)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Matches(@"^\d{10}$").WithMessage("{PropertyName} " + Messages.IsNotValid);
RuleFor(x => x.Password)
.NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty)
.Equal(x => x.ConfirmPassword).WithMessage(Messages.PasswordsDoNotMatch);
RuleFor(x => x.ConfirmPassword).NotEmpty().WithMessage("{PropertyName} " + Messages.CannotBeEmpty);
}
// Перевірка, чи має число рівно 11 цифр
private bool Be11Digits(long citizenId)
{
return citizenId.ToString().Length == 11;
}
// Користувацька логіка валідації
private bool ValidateCitizenId(long citizenId)
{
string citizenIdStr = citizenId.ToString();
// Перетворення в масив цифр
int[] digits = citizenIdStr.Select(digit => int.Parse(digit.ToString())).ToArray();
// Обчислення суми перших 10 цифр
int sum = digits.Take(10).Sum();
// Перевірка, чи сума за модулем 11 дорівнює останній цифрі
return sum % 10 == digits[10];
}
}
}
У цьому проєкті використовуються приклади основних правил (NotEmpty, Length, Must, When, EmailAdress, Matches, Equal
) та користувацьких методів (Be11Digits, ValidateCitizenId
). WithMessages
: Користувацькі повідомлення використовують {PropertyName}
для динамічного іменування властивостей і Messages
для узгодженого локалізованого повідомлення про помилки, яке показується клієнту.
Реєстрація валідатора
Якщо ви використовуєте Web API, вам потрібно зареєструвати ваш валідатор в Service Provider у класі Web API Program.cs (.Net Core 8 Project). Валідація автоматично активується у ваших діях контролера без необхідності вручну викликати валідатори.
var builder = WebApplication.CreateBuilder(args);
// Додайте сервіси в контейнер.
builder.Services.AddControllers();
builder.Services.AddBusinessStartup();
Приклад використання
Коли ви робите POST-запит з неправильними введеними даними, ви отримаєте результат ValidationException.
Висновок
FluentValidation — це чудовий інструмент для спрощення валідації в додатках .NET Core.
Окремо виділяючи логіку валідації від решти вашого бізнес-коду, ви створюєте більш чисті та зручні для підтримки додатки.
Сподіваюся, що ця стаття допомогла вам зрозуміти, як побудувати додаток з Fluent Validation у .NET Core! Повний вихідний код ви можете знайти на моєму репозиторії GitHub. Не соромтеся додавати зірочки, форкати або вносити свій внесок у проєкт.
https://github.com/58mustafasahin/SahinStockManager
Щасливого кодування!
Перекладено з: Fluent Validation: How To Register All Validators in .NET Core Project