Validation is one of the most important parts of API development. Without proper validation, applications may store invalid, incomplete, or harmful data in the database.
Although ASP.NET Core provides built-in model validation using Data Annotations, many developers prefer FluentValidation because it offers:
- cleaner validation logic
- better readability
- reusable rules
- advanced validation support
What is FluentValidation?
FluentValidation is a popular .NET library for building strongly-typed validation rules using a fluent interface.
Instead of decorating models with attributes like:
[Required]
[StringLength(50)]
FluentValidation allows writing validation rules in separate classes.
Why Use FluentValidation?
Advantages
- Clean separation of concerns
- Easy to maintain
- Better readability
- Reusable validation logic
- Supports complex validations
- Custom error messages
- Conditional validation
- Async validation support
Create ASP.NET Core Web API
Create a new project:
dotnet new webapi -n FluentValidationDemo
Move into project:
cd FluentValidationDemo
Install FluentValidation Package
Install the NuGet package:
dotnet add package FluentValidation.AspNetCore
Create Model Class
Example model:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public string Email { get; set; }
}
Create Validator Class
Create a folder named:
Validators
Now create:
ProductValidator.cs
Example:
using FluentValidation;
public class ProductValidator : AbstractValidator<Product>
{
public ProductValidator()
{
RuleFor(x => x.Name)
.NotEmpty()
.WithMessage("Product name is required")
.MaximumLength(50);
RuleFor(x => x.Price)
.GreaterThan(0)
.WithMessage("Price must be greater than zero");
RuleFor(x => x.Stock)
.GreaterThanOrEqualTo(0);
RuleFor(x => x.Email)
.EmailAddress()
.WithMessage("Invalid email format");
}
}
Register FluentValidation
In .NET 6/7/8 using Program.cs:
builder.Services.AddControllers();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining<ProductValidator>();
Add namespace:
using FluentValidation;
using FluentValidation.AspNetCore;
Create API Controller
Example controller:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpPost]
public IActionResult Create(Product product)
{
return Ok(new
{
Message = "Product created successfully",
Data = product
});
}
}
Test Validation
Valid Request
{
"name": "Laptop",
"price": 50000,
"stock": 10,
"email": "admin@test.com"
}
Invalid Request
{
"name": "",
"price": -10,
"stock": -5,
"email": "wrong-email"
}
Validation Response
ASP.NET Core automatically returns:
{
"errors": {
"Name": [
"Product name is required"
],
"Price": [
"Price must be greater than zero"
],
"Stock": [
"'Stock' must be greater than or equal to '0'."
],
"Email": [
"Invalid email format"
]
}
}
Common FluentValidation Rules
| Rule | Purpose |
|---|---|
NotEmpty() |
Required field |
NotNull() |
Prevent null |
Length() |
String length |
MaximumLength() |
Max characters |
MinimumLength() |
Min characters |
EmailAddress() |
Validate email |
GreaterThan() |
Numeric validation |
Equal() |
Match value |
Must() |
Custom validation |
Conditional Validation
Example:
RuleFor(x => x.Stock)
.GreaterThan(0)
.When(x => x.Price > 1000);
This validates stock only if price exceeds 1000.
Custom Validation
Example:
RuleFor(x => x.Name)
.Must(name => name.StartsWith("P"))
.WithMessage("Name must start with P");
Async Validation
Useful for database checks.
Example:
RuleFor(x => x.Email)
.MustAsync(async (email, cancellation) =>
{
return await IsEmailUnique(email);
});
Nested Object Validation
FluentValidation supports complex models.
Example:
RuleFor(x => x.Address)
.SetValidator(new AddressValidator());
Benefits Over Data Annotations
| Data Annotations | FluentValidation |
|---|---|
| Validation inside model | Separate validation class |
| Limited flexibility | Highly flexible |
| Harder to test | Easy unit testing |
| Less readable | Cleaner syntax |
Best Practices
Keep Validators Separate
Store validators inside dedicated folders.
Use Meaningful Messages
Bad:
Invalid input
Good:
Price must be greater than zero
Reuse Validators
Avoid duplicate validation logic.
Use Async Validation Carefully
Database checks can affect performance.
Real-World Example
Suppose an e-commerce API receives:
- product creation requests
- order data
- customer registration
- Without validation:
- negative prices
- invalid emails
- empty names
may enter the database.
Using FluentValidation ensures:
- cleaner API responses
- secure input handling
- reliable business rules
Conclusion
FluentValidation is a powerful and clean way to implement validation in ASP.NET Core APIs. It improves maintainability, readability, and flexibility compared to traditional Data Annotations.
By using FluentValidation, developers can:
- centralize validation logic
- create reusable rules
- build cleaner APIs
- improve application reliability
For modern ASP.NET Core applications, FluentValidation has become one of the most preferred validation libraries in enterprise development.
Leave a Comment