FluentValidation là thư viện validation phổ biến cho .NET, cho phép định nghĩa quy tắc validation tách biệt khỏi model (POCO), sử dụng fluent API dễ đọc, dễ bảo trì.
Tại sao dùng FluentValidation?
- Separation of Concerns: Validation logic tách khỏi domain model.
- Fluent API: Dễ đọc, chainable, IntelliSense tốt.
- Testable: Validator là class riêng, dễ unit test.
- Rich built-in validators: NotNull, Email, Length, Regex, InclusiveBetween, CreditCard...
- Custom validators:
Must,Custom,CustomAsync. - Integration: ASP.NET Core, Blazor, WPF, MAUI, MediatR, Minimal APIs.
Cài đặt
dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore // Cho ASP.NET Core integration
Ví dụ cơ bản
using FluentValidation;
using System;
public class User
{
public string Username { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public string Password { get; set; }
}
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(u => u.Username)
.NotEmpty().WithMessage("Username is required")
.Length(3, 20).WithMessage("Username must be 3-20 chars")
.Matches("^[a-zA-Z0-9_]+$").WithMessage("Username only alphanumeric/underscore");
RuleFor(u => u.Email)
.NotEmpty().WithMessage("Email is required")
.EmailAddress().WithMessage("Invalid email format");
RuleFor(u => u.Age)
.InclusiveBetween(18, 100).WithMessage("Age must be 18-100");
RuleFor(u => u.Password)
.NotEmpty().WithMessage("Password required")
.MinimumLength(8).WithMessage("Password min 8 chars")
.Matches("[A-Z]").WithMessage("Password needs uppercase")
.Matches("[0-9]").WithMessage("Password needs digit");
}
}
Chạy validation
var user = new User { Username = "ab", Email = "invalid", Age = 15, Password = "weak" };
var validator = new UserValidator();
ValidationResult result = validator.Validate(user);
if (!result.IsValid)
{
foreach (var error in result.Errors)
Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
}
Kết quả
Username: Username must be 3-20 chars
Email: Invalid email format
Age: Age must be 18-100
Password: Password min 8 chars
Password: Password needs uppercase
Password: Password needs digit
ASP.NET Core Integration
// Program.cs
builder.Services.AddControllers()
.AddFluentValidationAutoValidation()
.AddFluentValidationClientsideAdapters();
builder.Services.AddValidatorsFromAssemblyContaining<Program>(); // Auto register
// Controller - tự động validate, trả 400 nếu invalid
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpPost]
public IActionResult Create(User user) => Ok(user); // ModelState tự validate
}
Advanced Features
- Conditional Validation:
.When(u => u.IsActive, () => RuleFor(...)) - Async Validation:
.MustAsync(async (x, ct) => await CheckUnique(x)) - Cross-property:
RuleFor(u => u.ConfirmPassword).Equal(u => u.Password) - Collections:
RuleForEach(u => u.Addresses).SetValidator(new AddressValidator()) - Localization:
WithMessage(() => Localizer["Key"])
Best Practice: Một validator per model, đặt cùng folder với model hoặc folder Validators/. Test validator độc lập.