🧼 Clean API Validation in .NET – FluentValidation + Global Filter Done Right

PriyaPriya
3 min read

Building a modern API? Don’t let your validation logic spiral into an unmaintainable mess. Combine the power of FluentValidation and a Global Filter to enforce clean, testable rules and return consistent, user-friendly error responses.

Let’s break it down πŸ‘‡


πŸ”„ Request Lifecycle – Validation Flow

graph TD
  A[Client Request] --> B[Model Binding]
  B --> C[FluentValidation]
  C -->|Validation Fails| D[Global Error Filter]
  D --> E[Formatted Error Response]
  C -->|Validation Passes| F[Controller Action]
  F --> G[Success Response]

πŸ” Why Use Both FluentValidation + Global Filter?

LayerPurpose
βœ… FluentValidationClean, reusable, testable rules (separated from domain/UI logic)
βœ… Global FilterCentralized formatting of validation errors for consistency
❌ Data AnnotationsInline and tightly coupled β€” not ideal for scalability/testability
βœ… Custom LogicUseful for DB lookups, async rules, and cross-field validations

🧩 Think of It Like This

  • FluentValidation β†’ What to validate?
    e.g. .NotEmpty(), .Must(...), .GreaterThan(...)

  • Global Filter β†’ How to respond?
    Format errors like this:

      {
        "success": false,
        "errors": [
          { "field": "Type", "message": "Invalid bus type" }
        ]
      }
    
  • Custom Validation β†’ Complex checks?
    Ideal for service/DB-based or async validation rules

  • Data Annotations β†’ Quick & dirty
    Avoid mixing validation with models in scalable apps


πŸ›  Best Practice Cheatsheet

ConcernBest Tool
Field-level validationβœ… FluentValidation
Cross-property logicβœ… FluentValidation
Uniform error outputβœ… Global Filter (IActionFilter/Middleware)
Async/complex logicβœ… Custom Validators or Middleware
One-off inline rules❌ Avoid Data Annotations

⚠️ Why NOT Use Just One?

❌ FluentValidation Alone?

Default output:

{
  "errors": {
    "field": ["Some error message"]
  }
}

No success: false, no flattened format, no UX consistency.

❌ Only Global Filter?

Manually writing validations = Repetitive, fragile code.

❌ Data Annotations?

  • ❌ Hard to test

  • ❌ No DI

  • ❌ Mixes concerns

  • ❌ Poor for complex rules


βœ… Real Example: FluentValidation + Global Filter

BusValidator.cs

public class BusValidator : AbstractValidator<Bus>
{
    private static readonly List<string> AllowedTypes = new() { "Mini", "DoubleDecker", "Electric" };

    public BusValidator()
    {
        RuleFor(x => x.BusName)
            .NotEmpty().WithMessage("Bus name is required");

        RuleFor(x => x.Type)
            .Must(type => AllowedTypes.Contains(type))
            .WithMessage("Invalid bus type");
    }
}

Global Filter Output

{
  "success": false,
  "errors": [
    { "field": "Type", "message": "Invalid bus type" }
  ]
}

πŸ§ͺ Unit Test Example (xUnit + FluentValidation.TestHelper)

public class BusValidatorTests
{
    private readonly BusValidator _validator = new();

    [Fact]
    public void Should_Have_Error_When_BusName_Is_Empty()
    {
        var model = new Bus { BusName = "", Type = "Mini" };
        var result = _validator.TestValidate(model);
        result.ShouldHaveValidationErrorFor(x => x.BusName);
    }
}

βœ… Final Comparison – When to Use What?

Validation TypeUse It?Why
FluentValidationβœ… YesClean, testable, supports DI
Global Filterβœ… YesCentralized error formatting
Custom Validationβœ… YesAsync logic, DB lookups, cross-entity rules
Data Annotations❌ NoObsolete in scalable architecture

πŸš€ Wrapping Up

Combining FluentValidation + Global Filter gives you:

βœ… Clean separation of concerns
βœ… Predictable, consistent client responses
βœ… Maintainable and testable validation logic

πŸ“Œ This pattern is a must-have for any professional .NET Web API project!


πŸ’‘ Bonus Tip: Middleware Over Filters?

Prefer middleware? You can handle validation globally via middleware too β€” ideal for advanced async or cross-cutting rules.
βœ‰οΈ Drop a comment if you'd like to see that version!


πŸ”— Useful Resources

1
Subscribe to my newsletter

Read articles from Priya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Priya
Priya