FluentValidation Documentation

repository·main·Indexed 27 days ago

https://github.com/fluentvalidation/fluentvalidation

A .NET validation library that uses a fluent interface and lambda expressions to build strongly-typed validation rules. It supports integration with Microsoft Dependency Injection, ASP.NET Core (MVC, Razor Pages, and Minimal APIs), and provides features such as custom validation logic via Must, PreValidate for pre-execution logic, and RootContextData for passing external data into the validation pipeline.

Tokens
24.8K
Snippets
73
Records
127
Agent score
94%

What's inside FluentValidation

  1. Include the collection index in error messages

    main

    When using RuleForEach, you can use the {CollectionIndex} placeholder in your error message to identify which specific element in the collection failed validation.

    public class PersonValidator : AbstractValidator<Person> 
    {
      public PersonValidator() 
      {
        RuleForEach(x => x.AddressLines).NotNull().WithMessage("Address {CollectionIndex} is required.");
      }
    }
  2. Automatically register validators using FluentValidation.DependencyInjectionExtensions

    main

    Use the FluentValidation.DependencyInjectionExtensions package to automatically scan an assembly and register all public, non-abstract validators.

    By default, validators are registered as Scoped. You can specify a different ServiceLifetime (such as Transient or Singleton) during registration.

    Warning: If registering as Singleton, ensure the validator does not inject Transient or Scoped dependencies. Registering as Transient is generally the safest option.

    using FluentValidation.DependencyInjectionExtensions;
    
    public void ConfigureServices(IServiceCollection services)
    {
        // Register all validators in the assembly containing UserValidator
        services.AddValidatorsFromAssemblyContaining<UserValidator>();
    
        // Register all validators in the assembly containing UserValidator as Transient
        services.AddValidatorsFromAssemblyContaining<UserValidator>(ServiceLifetime.Transient);
    
        // Alternative: Use a type instance instead of a generic
        services.AddValidatorsFromAssemblyContaining(typeof(UserValidator));
    
        // Alternative: Use an assembly reference
        services.AddValidatorsFromAssembly(Assembly.Load("SomeAssembly"));
    }
  3. Choose the correct FluentValidation version for your .NET runtime

    main

    Select the version of FluentValidation based on your project's target framework:

    • FluentValidation 12: Use this for .NET 8 and newer (including .NET 10).
    • FluentValidation 11: Use this if you need support for older runtimes, including .NET Standard 2.0, .NET Core 3.1, and .NET 5 and newer.
  4. Validate polymorphic properties using SetInheritanceValidator

    main

    When a property is defined as a base class or an interface, you can use SetInheritanceValidator to apply specific validators based on the object's actual runtime type. This allows you to define unique rules for different subclasses or implementations of that interface.

    To use it, call SetInheritanceValidator on a RuleFor chain and use the Add<T> method within the provided callback to map specific types to their corresponding validators.

    public class ContactRequestValidator : AbstractValidator<ContactRequest>
    {
      public ContactRequestValidator()
      {
        RuleFor(x => x.Contact).SetInheritanceValidator(v => 
        {
          v.Add<Organisation>(new OrganisationValidator());
          v.Add<Person>(new PersonValidator());
        });
      }
    }
  5. Validate sub-properties of collections using wildcard indexers

    main

    To validate properties within items of a collection using IncludeProperties, use a wildcard indexer ([]) in the property path string. For example, to validate the Cost property of every item in an Orders collection, use the path string "Orders[].Cost".

    var validator = new CustomerValidator();
    validator.Validate(customer, options => 
    {
      options.IncludeProperties("Orders[].Cost");
    });
  6. Update ASP.NET Client Validator Adaptors

    main

    The signature for adding ASP.NET Client Validator factories has changed in FluentValidation 10.0. Factories now receive an IValidationRule and an IRuleComponent instead of a PropertyRule and IPropertyValidator. Additionally, because property validators are now generic, it is recommended to use the non-generic interface (e.g., IMyCustomPropertyValidator) as the lookup key.

    // After migration
    public class MyCustomClientsideAdaptor : ClientValidatorBase
    {
      public MyCustomClientsideAdaptor(IValidationRule rule, IRuleComponent component)
      : base(rule, component)
      {
      }
    
      public override void AddValidation(ClientModelValidationContext context)
      {
        // ...
      }
    }
    
    services.AddMvc().AddFluentValidation(fv =>
    {
      fv.ConfigureClientsideValidation(clientSide =>
      {
        clientSide.Add(typeof(IMyCustomPropertyValidator), (context, rule, component) => new MyCustomClientsideAdaptor(rule, component));
      })
    })
  7. Limit validation to specific properties using IncludeProperties

    main

    When a validator contains rules for multiple properties, you can restrict execution to only a subset of those properties by using the IncludeProperties option within the Validate method. This is useful for partial updates or performance optimization.

    // Validator definition
    public class CustomerValidator : AbstractValidator<Customer>
    {
      public CustomerValidator()
      {
        RuleFor(x => x.Surname).NotNull();
        RuleFor(x => x.Forename).NotNull();
        RuleForEach(x => x.Orders).SetValidator(new OrderValidator());
      }
    }
    
    // Usage
    var validator = new CustomerValidator();
    validator.Validate(customer, options => 
    {
      options.IncludeProperties(x => x.Surname);
    });
  8. Perform manual validation in ASP.NET Core controllers

    main

    Manual validation involves injecting IValidator<T> into your controller or Razor page and explicitly calling ValidateAsync.

    If validation fails, you must manually transfer the errors to the ASP.NET ModelState so they can be displayed in the UI. You can implement an extension method AddToModelState on ValidationResult to simplify this process.

    public class PeopleController : Controller 
    {
      private IValidator<Person> _validator;
      private IPersonRepository _repository;
    
      public PeopleController(IValidator<Person> validator, IPersonRepository repository) 
      {
        _validator = validator;
        _repository = repository;
      }
    
      [HttpPost]
      public async Task<IActionResult> Create(Person person) 
      {
        ValidationResult result = await _validator.ValidateAsync(person);
    
        if (!result.IsValid) 
        {
          // Copy the validation results into ModelState for the View
          result.AddToModelState(this.ModelState);
          return View("Create", person);
        }
    
        _repository.Save(person);
        return RedirectToAction("Index");
      }
    }
    
    // Extension method to bridge FluentValidation and ASP.NET ModelState
    public static class Extensions 
    {
      public static void AddToModelState(this ValidationResult result, ModelStateDictionary modelState) 
      {
        foreach (var error in result.Errors) 
        {
          modelState.AddModelError(error.PropertyName, error.ErrorMessage);
        }
      }
    }
  9. Configure ValidatorAttribute via separate package

    main

    The ValidatorAttribute and AttributedValidatorFactory have been moved to a separate package: FluentValidation.ValidatorAttribute.

    • ASP.NET Core: Use the service provider to wire models to validators (recommended).
    • Desktop/Mobile: Use an IoC container (recommended). If you must use attributes, install the FluentValidation.ValidatorAttribute package.
    • Legacy ASP.NET (MVC 5/WebApi 2): The FluentValidation.ValidatorAttribute package is automatically installed for compatibility, but using an IoC container is recommended.
  10. Implement complex validation logic with the Custom method

    main

    If you need more control than Must provides—such as returning multiple validation failures for a single rule—use the Custom method.

    Inside the Custom callback, you can manually call context.AddFailure() to register errors. You can specify a custom property name for the failure or pass a ValidationFailure object directly.

    public class PersonValidator : AbstractValidator<Person> {
      public PersonValidator() {
        RuleFor(x => x.Pets).Custom((list, context) => {
          if(list.Count > 10) {
            // Adds failure to the property being validated
            context.AddFailure("The list must contain 10 items or fewer");
            
            // Or add failure to a different property
            context.AddFailure("SomeOtherProperty", "The list must contain 10 items or fewer");
          }
        });
      }
    }
  11. Automatic validation via ASP.NET Validation Pipeline

    main

    The FluentValidation.AspNetCore package allows for automatic validation by plugging into the built-in ASP.NET Core MVC validation pipeline. This validates models during model-binding before the controller action is executed.

    Warning: This approach is no longer recommended for new projects.

    Limitations:

    • Synchronous only: It cannot run asynchronous validation rules. Attempting to use async rules will result in a runtime exception.
    • MVC/Razor Pages only: It does not work with Minimal APIs or Blazor.
    • Harder to debug: The automatic nature makes troubleshooting difficult.
  12. Configure Rule-Level Cascade Modes

    main

    Rule-level cascade modes control how FluentValidation executes a chain of validators within a single rule definition (e.g., RuleFor(x => x.Prop).NotNull().NotEqual("foo")).

    • CascadeMode.Continue (Default): Always invokes all validators in the chain, even if one fails.
    • CascadeMode.Stop: Stops executing the chain as soon as a single validator fails. This is useful for preventing subsequent validators from running if they depend on the success of previous ones (e.g., avoiding a null reference exception).

    You can set this for a specific rule using .Cascade(CascadeMode.Stop) or set a default for all rules within a specific validator class using the RuleLevelCascadeMode property.

    // Specific rule level
    RuleFor(x => x.Surname).Cascade(CascadeMode.Stop).NotNull().NotEqual("foo");
    
    // Validator class level default
    public class PersonValidator : AbstractValidator<Person> {
      public PersonValidator() {
        RuleLevelCascadeMode = CascadeMode.Stop;
        RuleFor(x => x.Forename).NotNull().NotEqual("foo");
      }
    }