Fluent Validation trong .Net

Fluent Validation trong .Net

May 4, 2025 0 By Nam Vu

Trong thế giới lập trình với .NET Core, chúng ta thường xuyên bắt gặp các công cụ và kỹ thuật giúp cuộc sống của lập trình viên trở nên dễ dàng hơn rất nhiều. Có một bộ công cụ giúp code clear và dễ dàng sử dụng hơn đó chính là Fluent Validation. Tuy nhiên, cũng như bất kỳ công cụ nào, bạn cũng cần hiểu rõ điểm mạnh và điểm yếu của nó khi bạn đang dùng.

Mình đã từng có một bài viết nói về nguyên lý lego Fluent Interface rồi. Bạn có thể đọc lại để hiểu nguyên lý trước khi đọc bài viết làm sao để implement ở bài viết này nhé https://blog.ntechdevelopers.com/fluent-interface-va-nguyen-ly-lod-huyen-thoai/

Sẵn sàng bắp rang chưa?

Chúng ta chỉ mới bắt đầu cùng khám phá vào Fluent Validation nào!

1. Đi đầu xu hướng với Fluent Validation trong .NET Core

Bạn có nghe thấy gì không? Đó chính là âm thanh của năng suất của bạn đang tăng vọt đấy!

  • Tăng hiệu suất lập trình với Fluent Validation

Phép màu bắt đầu khi bạn tích hợp Fluent Validation vào dự án. Công cụ này giúp bạn xây dựng ứng dụng gọn gàng hơn, loại bỏ sự phụ thuộc vào cơ sở dữ liệu hoặc giao diện, và tập trung hoàn toàn vào logic nghiệp vụ.

Ví dụ đơn giản:

public class CustomerValidator : AbstractValidator <Customer>
{
  public CustomerValidator()
  {
    RuleFor(customer => customer.Name).NotEmpty()
      .WithMessage("Vui lòng nhập tên");
  }
}

Từ ví dụ này, bạn có thể thấy Fluent Validation cho phép bạn viết quy tắc kiểm tra dữ liệu ngay trong lớp nghiệp vụ. Không cần quan tâm đến giao diện hay database mà chỉ tập trung vào phần cốt lõi logic.

  • Tăng tốc vòng đời phát triển phần mềm

Không ai muốn lãng phí thời gian cả. Với Fluent Validation, bạn như đang khoác áo choàng siêu nhân, viết code nhanh như chớp.

Còn debugging, “người bạn thù ghét lâu năm” thì sao? Fluent Validation giúp bạn giảm thời gian xử lý lỗi, và nhờ đó bạn có thêm thời gian… uống cà phê!

  • Sự linh hoạt và mở rộng mạnh mẽ

Linh hoạt trong quy tắc kiểm tra dữ liệu

Trong lập trình (và cả cuộc sống), linh hoạt là yếu tố sống còn. Fluent Validation cho phép bạn viết quy tắc kiểm tra rất linh hoạt.

Ví dụ mở rộng:

public class UserValidator: AbstractValidator <User>
{
    public UserValidator()
    {
        RuleFor(user => user.UserName).Matches(@ "^[a-zA-Z][a-zA-Z0-9]*$").WithMessage("Tên người dùng không hợp lệ.");
        RuleFor(user => user.Password).Matches(@ "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+=]).{8,}$").WithMessage("Mật khẩu phải mạnh và đủ 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt.");
        RuleFor(user => user.Email).EmailAddress().WithMessage("Email không hợp lệ.");
        RuleFor(user => user.DateOfBirth).Must(BeAValidAge).WithMessage("Bạn phải từ 18 tuổi trở lên.");
    }
    private bool BeAValidAge(DateTime dob)
    {
        int age = DateTime.Today.Year - dob.Year;
        if (dob.Date > DateTime.Today.AddYears(-age)) age--;
        return age >= 18;
    }
}

Các quy tắc trên rất chi tiết: từ định dạng tên người dùng đến độ mạnh của mật khẩu, email, và tuổi tối thiểu.

Mở rộng với validator tùy chỉnh

Bạn có thể tự tạo validator riêng, ví dụ:

public class CustomValidator: AbstractValidator <User>
{
    public CustomValidator()
    {
        RuleFor(user => user).Custom((user, context) =>
        {
            if (user.Password.StartsWith(user.FirstName))
            {
                context.AddFailure("Password", "Mật khẩu không được bắt đầu bằng tên.");
            }
            if (user.Password.StartsWith(user.LastName))
            {
                context.AddFailure("Password", "Mật khẩu không được bắt đầu bằng họ.");
            }
        });
    }
}

Bạn có thể xử lý logic tùy chỉnh dễ dàng và rõ ràng.

  • Tăng độ dễ đọc và dễ bảo trì mã nguồn

Giữ code gọn gàng cũng giống như giữ phòng sạch sẽ — giúp bạn dễ dàng làm việc và tránh rối rắm về sau.

Ví dụ:

public class RegistrationValidator: AbstractValidator <Registration>
{
    public RegistrationValidator()
    {
        RuleFor(reg => reg.AccountName).NotEmpty().Length(5, 14).WithMessage("Tên tài khoản từ 5-14 ký tự");
        RuleFor(reg => reg.Password).NotEmpty().Length(8, 15).WithMessage("Mật khẩu từ 8-15 ký tự");
        RuleFor(reg => reg.Email).EmailAddress().WithMessage("Email không hợp lệ");
    }
}

Mọi quy tắc nằm gọn trong một lớp, không lộn xộn khắp nơi và đọc là hiểu ngay.

  • Dễ cập nhật và bảo trì

Giả sử PM yêu cầu thêm quy tắc mật khẩu phải có ký tự đặc biệt, chữ và số. Ta chỉ cần sửa 1 dòng:

RuleFor(reg => reg.Password)
.NotEmpty()
.Length(8, 15)
.Matches(@ "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^a-zA-z\\d\\s:]).*")
.WithMessage("Mật khẩu phải chứa ít nhất một chữ, một số và một ký tự đặc biệt.");

Thế là xong, gọn lẹ!

2. Đơn giản hóa kiểm thử với Fluent Validation

Fluent Validation rất hợp để viết Unit Test vì logic kiểm tra dữ liệu đã được gom lại một nơi, dễ gọi, dễ test.

Ví dụ kiểm thử với xUnit:

public class CustomerValidatorTests
{
    private readonly CustomerValidator _validator = new CustomerValidator();
    [Fact]
    public void Should_Fail_When_Name_Is_Empty()
    {
        var customer = new Customer
        {
            Name = string.Empty,
                Email = "[email protected]",
                Password = "password",
                Address = "100 Main st."
        };
        var result = _validator.Validate(customer);
        Assert.False(result.IsValid);
    }
}

Chúng ta chỉ cần truyền vào dữ liệu không hợp lệ và kiểm tra kết quả.

Kết quả dễ dự đoán

Fluent Validation mang lại sự ổn định. Với mỗi rule, bạn biết chắc điều kiện nào đúng, điều kiện nào sai.

public class CustomerValidator: AbstractValidator <Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.Name).NotEmpty().Length(1, 100);
        RuleFor(customer => customer.Email).EmailAddress();
        RuleFor(customer => customer.Password).NotEmpty().Length(6, 20);
        RuleFor(customer => customer.Address).Length(0, 500);
    }
}

3. Những hạn chế tiềm ẩn của Fluent Validation

  • Độ khó ban đầu

Cú pháp “fluent” ban đầu có thể khiến bạn hơi choáng, đặc biệt nếu bạn mới tiếp cận .NET:

RuleFor(c => c.Name)
.NotEmpty()
.Length(1, 100)
.WithMessage("Tên phải từ 1 đến 100 ký tự");

Tuy nhiên, bạn sẽ nhanh chóng quen với cú pháp này, và lợi ích đem lại thì rất xứng đáng.

  • Ảnh hưởng hiệu năng

Nếu validator có quá nhiều rule phức tạp, mỗi lần gọi .Validate() có thể hơi chậm, đặc biệt với nhiều bản ghi hoặc model lớn.

RuleFor(c => c.Name)
.NotEmpty()
.Length(1, 100)
.WithMessage("Tên phải từ 1 đến 100 ký tự");
public class InviteValidator: AbstractValidator <Invite>
{
    public InviteValidator()
    {
        RuleFor(invite => invite.Email).NotEmpty().EmailAddress();
        RuleFor(invite => invite.Code).NotEmpty().Length(10);
    }
}

Tuy nhiên, viết validator một cách hợp lý sẽ giảm thiểu vấn đề này.

  • Chi phí

Thời điểm mình viết bài viết này thì Fluent Validation còn miễn phí, nhưng thời gian gần đây nó chuyển từ license miễn phía sang có phí khiến anh em trong cộng đồng xôn xao hết cả. Đây có thể coi là một điểm mà anh em cân nhắc nên mình liệt kê nó vào nhược điểm

Tóm lại, Fluent Validation là một công cụ mạnh mẽ trong kho vũ khí của bạn khi làm việc với .NET Core. Nhưng như mọi công cụ, nó cũng có ưu và nhược điểm riêng. Việc hiểu rõ điểm mạnh cũng như điểm yếu sẽ giúp bạn sử dụng nó đúng cách, đúng chỗ, và đạt hiệu quả cao nhất.

Hi vong bài viết có thể giúp bạn hiểu được cách implement của Fluent Validation trong logic validate của bạn!

#ntechdevelopers