FluentValidation is a validation library for .NET that allows you to validate model objects and properties using a fluent API. It's widely used for model validation in ASP.NET applications and web APIs. This guide will provide a complete overview of how to implement validation with FluentValidation in .NET Core projects.
What is FluentValidation?
FluentValidation is an open source validation library that aims to make validation classes more readable and maintainable. The key features that differentiate it from other validation frameworks are:
- Fluent API: Validation rules are defined using a fluent or natural language-like API instead of attributes or per-property callbacks. This makes validation configurations more readable.
- Reusable Validators: Validator classes can be defined and reused for different model objects. This avoids duplicating validation logic.
- Runtime Evaluation: Validation occurs at runtime rather than at compile-time like with attributes. This allows you to define complex validation rules.
- External Validation: Integrations allow validation to be performed externally like with a database or web service.
- Cross-Platform: Works across .NET Framework, .NET Core, Mono and Xamarin.
So in summary, FluentValidation is beneficial for defining advanced validation logic in a readable and reusable manner at runtime across .NET applications.
Getting Started
To use FluentValidation in a .NET Core project, first install the NuGet package:
Copy
dotnet add package FluentValidation
Next, define a class that inherits from AbstractValidator<T>
where T
is the model class you want to validate:
csharp
Copy
public class ProductValidator : AbstractValidator<Product> {
public ProductValidator() {
// validation rules
}
}
Then inside the constructor define validation rules using the fluent API:
csharp
Copy
RuleFor(p => p.Name)
.NotEmpty()
.WithMessage("Name is required");
RuleFor(p => p.Price)
.GreaterThan(0)
.WithMessage("Price must be greater than zero");
Finally, validate the model by calling Validate()
on the validator:
csharp
Copy
var validator = new ProductValidator();
ValidationResult result = validator.Validate(product);
if(!result.IsValid)
// handle errors
This provides the basic setup needed to start validating models with FluentValidation.
Validation Rules
FluentValidation supports a wide range of built-in validation rules that can be applied to model properties:
- NotEmpty() - Ensures the property is not null, empty or whitespace
- NotNull() - Ensures the property is not null
- GreaterThan(x) - Ensures the property value is greater than a specified value
- InclusiveBetween(min, max) - Ensures the property value falls within a range
- Length(min, max) - Ensures the property length is within a range
- EmailAddress() - Validates email format
- CreditCard() - Validates credit card number format
- Matches(pattern) - Matches property value against a regular expression
- Regex(pattern) - Same as Matches()
Custom validation logic can also be implemented by overriding IValidator<T>.Validate()
or using the Custom()
rule:
csharp
Copy
RuleFor(p => p.SKU)
.Custom((sku, context) =>
{
if(skuExistsInDatabase(sku))
context.AddFailure("SKU already exists");
});
Conditional validation is supported with When()
- rules inside are only evaluated if the condition is true:
csharp
Copy
RuleFor(p => p.Country)
.Equal("US")
.When(p => p.RequiresCountry);
This covers some of the main built-in validation rules supported.
Custom Validation Messages
By default, validation failures will display generic error messages. To customize these:
- Call
WithMessage()
when defining the rule to set a custom failure message - Create a localized error message resource file
- Reference error messages by name in
WithMessage()
For example:
csharp
Copy
resources.Add("NameIsRequired", "Name is a required field");
RuleFor(p => p.Name)
.NotEmpty()
.WithMessage("NameIsRequired");
This allows internationalization of error messages as well.
Validation Groups
Sometimes you want to validate different properties depending on usage scenarios. FluentValidation supports validation groups to specify which properties should be validated:
csharp
Copy
public interface IUpdateModel { }
// omit properties not being updated
RuleFor(p => p.Name)
.SetValidatorGroup(typeof(IUpdateModel));
Then pass the group interface to Validate()
:
csharp
Copy
var result = validator.Validate(product, rules => rules.Include(typeof(IUpdateModel)));
This is useful for partial model binding scenarios.
Cascading Validation
Complex object graphs can be validated recursively with cascading validation:
csharp
Copy
public class Order
{
public string Name { get; set; }
public List<OrderItem> Items { get; set; }
}
public class OrderItem
{
public string Name { get; set; }
public int Quantity { get; set; }
}
The order validator would validate both order properties and items:
csharp
Copy
RuleForEach(o => o.Items)
.SetValidator(new OrderItemValidator());
Now any item validation errors will also be included in the overall result.
Integrating with ASP.NET Core
In ASP.NET Core MVC/Web API projects, FluentValidation integrates seamlessly with model binding:
- Add
FluentValidationMvc
to dependencies - Register all validators as services:
csharp
Copy
services.AddTransient<IValidator<Product>, ProductValidator>();
3.Annotate controller action parameters:
csharp
Copy
[HttpPost]
public IActionResult Create([FromBody] Product product) {
// validation automatically performed
if(!ModelState.IsValid)
return BadRequest(ModelState);
// save product
return Ok();
}
ModelState is automatically populated with any validation errors.
So in summary, FluentValidation provides a powerful yet easy to use validation solution for .NET models. The fluent API makes rules readable while features like groups, cascading and integrations enhance functionality. It's a great choice for model validation in ASP.NET Core and other .NET applications.
Comments