How to use FluentValidation in ASP.NET Core

By FoxLearn 12/30/2024 7:50:38 AM   52
FluentValidation is an open-source library for validating data in ASP.NET Core applications, helping to maintain clean, organized, and maintainable code.

It ensures the integrity, consistency, and security of application data by validating that data is received in the correct format. The library offers a fluent interface, which is an object-oriented API that allows easy chaining of method calls to build strongly typed validation rules in .NET applications.

This article introduces FluentValidation, explains its benefits, and demonstrates how to use it in ASP.NET applications. It emphasizes how FluentValidation helps achieve clean, elegant, flexible, and maintainable validation logic.

What is FluentValidation?

FluentValidation is an open-source library for .NET that allows you to create strongly typed validation rules with a fluent interface, improving code readability through method chaining and flowing expressions.

Why should we use it?

  • Simplicity: Its intuitive syntax makes validation rules easy to write, understand, and maintain.
  • Flexibility: It supports both simple and complex validation rules, as well as custom validation methods.
  • Separation of concerns: It helps isolate validation logic from business logic, making the code cleaner and more maintainable.
  • Integration: FluentValidation easily integrates with frameworks like ASP.NET and ASP.NET Core.

The problem with data annotations is that they require decorating classes with attributes to enforce validation rules, which can lead to cluttered and less maintainable code. This approach can make validation logic less flexible and harder to manage as the application grows.

public class Product
{
    [Required(ErrorMessage = "ProductName is required.")]
    [Display(Name = "Product Name")]
    public string ProductName { get; set; }

    [Required(ErrorMessage = "Category is required.")]
    [Display(Name = "Category")]
    public string Category { get; set; }

    [Range(0.01, 10000.00, ErrorMessage = "Price must be between $0.01 and $10,000.")]
    [Display(Name = "Price")]
    public decimal Price { get; set; }

    [Range(0, 1000, ErrorMessage = "Stock quantity must be between 0 and 1000.")]
    [Display(Name = "Stock Quantity")]
    public int StockQuantity { get; set; }

    [StringLength(500, ErrorMessage = "Description can't be longer than 500 characters.")]
    [Display(Name = "Product Description")]
    public string Description { get; set; }
}

The downside of using data annotations is that validation rules are tightly coupled with your model classes, which violates the separation of concerns principle. This means that the validation logic is mixed with your domain logic. In contrast, FluentValidation allows you to easily separate these concerns, keeping validation logic separate from your business logic.

To use the FluentValidation library, you need to install the FluentValidation NuGet package in your project.

You can do this by right-clicking the project in the Solution Explorer, selecting "Manage NuGet Packages," searching for FluentValidation, and installing it.

Alternatively, you can install the package using the NuGet Package Manager console with the following command:

PM> Install-Package FluentValidation

With FluentValidation, you can validate data for an instance of the Product class in a cleaner way. First, remove all the data annotation attributes from the properties of the Product class.

After applying FluentValidation, the Product class becomes simplified, as the validation logic is moved to a separate validation class instead of being tightly coupled with the model.

public class Product
{
    public string ProductName { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
    public string Description { get; set; }
}

Create a new class called ProductValidator that extends the AbstractValidator class. This class will contain validation rules for the Product model, using FluentValidation's fluent syntax to define and enforce validation logic.

using FluentValidation;

public class ProductValidator : AbstractValidator<Product>
{
    public ProductValidator()
    {
        RuleFor(x => x.ProductName).NotNull().WithMessage("Product name cannot be null or empty");
        RuleFor(x => x.Category).NotNull().WithMessage("Category cannot be null or empty");
        RuleFor(x => x.Price).GreaterThan(0).WithMessage("Price must be greater than zero");
        RuleFor(x => x.StockQuantity).GreaterThanOrEqualTo(0).WithMessage("Stock quantity cannot be negative");
        RuleFor(x => x.Description).MaximumLength(500).WithMessage("Description can't be longer than 500 characters");
    }
}

To validate input data, you can create an HTTP Post endpoint called /validateproduct. This endpoint will handle the validation of author data, typically by receiving input, applying the validation rules, and returning the validation results.

app.MapPost("/validateproduct", async (Product request, IValidator validator) =>
{
    var validationResult = await validator.ValidateAsync(request);
    if (!validationResult.IsValid)
    {
        return Results.ValidationProblem(validationResult.ToDictionary());
    }
    return Results.Accepted();
});

Register the validation class in ASP.NET Core

Finally, you should register the ProductValidator in the Program.cs file to ensure that it can be used for validation within the application.

builder.Services.AddScoped, ProductValidator>();

FluentValidation is an excellent choice for implementing complex custom validation rules in domain models or data transfer objects. In this article, we've utilized the built-in validators provided by FluentValidation.