ASP.NET Core Web API

FluentValidation Integration

1 Views Updated 5/4/2026

FluentValidation Integration

While standard Data Annotations are simple, they have severe limitations. They clutter your clean DTO classes, they cannot easily validate parent-child object structures, and writing complex conditional logic (e.g., "CreditCardNumber is required ONLY IF PaymentMethod is 'Card'") is agonizing. FluentValidation is the enterprise standard that completely replaces DataAnnotations.

1. Installation & The Separation of Concerns

FluentValidation removes validation rules from the DTO entirely, placing them into dedicated "Validator" classes using a clean, readable lambda-expression syntax.

dotnet add package FluentValidation.AspNetCore

Step 1: The Clean DTO

public class RegistrationDto
{
    // No messy attributes here!
    public string Email { get; set; }
    public string Password { get; set; }
    public string StateProvince { get; set; }
    public string ZipCode { get; set; }
}

Step 2: The Validator Class

using FluentValidation;

public class RegistrationValidator : AbstractValidator<RegistrationDto>
{
    public RegistrationValidator()
    {
        // Chainable, beautiful syntax
        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("Email is required.")
            .EmailAddress().WithMessage("Must be a valid email.");

        RuleFor(x => x.Password)
            .MinimumLength(8).WithMessage("Password too short.");

        // Complex Conditional Logic (Impossible with standard DataAnnotations)
        // Require ZipCode ONLY IF the State is 'CA' or 'NY'
        RuleFor(x => x.ZipCode)
            .NotEmpty()
            .When(x => x.StateProvince == "CA" || x.StateProvince == "NY")
            .WithMessage("Zip code is mandatory for California and New York residents.");
    }
}

2. Connecting FluentValidation to ASP.NET Core

To make the [ApiController] automatic 400 Bad Request feature seamlessly trigger these new Fluent rules instead of standard annotations, we register FluentValidation in Program.cs.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

// Automatically scans the assembly for all AbstractValidator classes and registers them
builder.Services.AddValidatorsFromAssemblyContaining<RegistrationValidator>();
builder.Services.AddFluentValidationAutoValidation();

3. Advanced: Database Validation (MustBeUnique)

One of FluentValidation's greatest strengths is its ability to inject Services (like a DbContext) directly into the validator to check database constraints asynchronously.

public class UserValidator : AbstractValidator<CreateUserDto>
{
    private readonly ApplicationDbContext _db;

    // Use DI in the Validator!
    public UserValidator(ApplicationDbContext db)
    {
        _db = db;

        RuleFor(x => x.Email)
            .NotEmpty()
            // MustAsync enables asynchronous database checks
            .MustAsync(BeUniqueEmail).WithMessage("This email is already registered.");
    }

    private async Task<bool> BeUniqueEmail(string email, CancellationToken token)
    {
        // Returns true if NO user exists with this email
        return !await _db.Users.AnyAsync(u => u.Email == email, token);
    }
}

4. Interview Mastery

Q: "Why do enterprise applications universally prefer FluentValidation over DataAnnotations?"

Architect Answer: "The primary driver is the Single Responsibility Principle. A DTO's ONLY job is to declare physical data layout so the JSON parser can hydrate it. It should not be burdened with business rules. Furthermore, FluentValidation allows for Contextual Validation: I can create two different Validators for the exact same `UserDto`—one for Administrators (who can leave the password blank when resetting it) and one for standard users (who must provide the old password). DataAnnotations are hardcoded onto the class, making situational validation nearly impossible without writing duplicate DTOs."

ASP.NET Core Web API
1. Fundamentals & HTTP
Introduction to ASP.NET Core Web API REST Principles and HTTP Methods Controllers & ControllerBase Routing (Attribute vs Conventional) Action Return Types (IActionResult)
2. Request Handling
Model Binding (FromQuery, FromBody, FromRoute) Dependency Injection (DI) Deep Dive App Settings & The Options Pattern
3. Data Access & Architecture
EF Core Setup in Web API DbContext & Migrations Repository & Unit of Work Pattern Asynchronous Programming (async/await)
4. Data Transfer & Validation
Data Transfer Objects (DTOs) & AutoMapper Model Validation (DataAnnotations) FluentValidation Integration
5. Advanced Concepts
Global Exception Handling Middleware Content Negotiation (JSON vs XML) Pagination & Filtering Advanced Searching & Sorting HATEOAS (Hypermedia) Implementation Output Caching & Response Caching
6. Security & Authorization
Cross-Origin Resource Sharing (CORS) JWT Authentication Setup Access Tokens & Refresh Tokens Workflow Role-Based & Policy-Based Authorization API Key Authentication Rate Limiting & Throttling
7. Documentation & Testing
Swagger & OpenAPI Configuration Customizing API Documentation Unit Testing Controllers (xUnit & Moq) Integration Testing (WebApplicationFactory)
8. Microservices & Deployment
Consuming External APIs (IHttpClientFactory) Health Checks & Diagnostics API Versioning Strategies Deploying APIs (Docker & Azure)