There are variety of ways in which business validations are applied. Some projects may have used various design patterns to build custom layers which would help to keep all the validations at one place.
Fluent validations library provides a way to organize your validations at single place. It also helps to keep the consistent syntax across various validations. This article is going to explain some basics about the fluent validation syntax. It will also explain how to setup fluent validations in ASP .NET Core Web API project.
Create the project
In this article, we are going to use Visual Studio 2022 and .NET 7 to create the Web API project. Let’s create a Web API Project. In the new project wizard, let’s keep the default settings selected and create the project.
The NuGet Package
Before using Fluent Validation in our project, we need to make sure that the NuGet package is added to the project. For integrating the fluent validation with ASP .NET Core project, the NuGet package – FluentValidation.AspNetCore
– needs to be added to the project.
This NuGet can be added by using .NET CLI command given below.
dotnet add package FluentValidation.AspNetCore
Alternatively, we can add the package by using Visual Studio. For that, right click on the project and select Manage NuGet Packages option from the context menu.
Then search for the package FluentValidation.AspNetCore
and then install it.
Create New API Endpoint
Now, in order to test the validation, let’s add a simple POST endpoint to the newly created project. Let’s say, we want to create an endpoint, which takes a custom type, Student, as input parameter. This Student
type has few properties, id, name and list of courses.
The code given below shows the Student
type and then it also shows the StudentController
which has the new endpoint.
Which validations should be added ?
Now, hypothetically, let’s say we want to below mentioned validations to the newly created endpoint:
- Let’s say, we want student id to be always greater than zero
- Then, the length of student name should be not more than 250 characters and it should not be less than 10 characters.
- For the courses collection, which is a string array, let’s say we want two validations
- For each entry, allowed minimum length is 8 and allowed maximum length is 100
- Courses collection should have more than 5 entries
How to add a new validator ?
Let’s see how to add the above mentioned validations using fluent validators. Let’s create a folder Validators and add a new validator there, StudentValidator
.
Base class
For creating custom validator, the custom validation class needs to be derived from a generic class, AbstractValidator<T>
. Here the type parameter T
should be the type that we want to validate. In our code example, we want to validate Student
type. Hence we would need to derive StudentValidator
from AbstractValidator<Student>
.
A basic validation rule
Another thing to note – all the validation rules must be described in the constructor. The method RuleFor
should be called for setting up the validation. This method takes a lambda expression as input, which should specify the property that we want to validate. This call can be chained to call other validators.
For example, let’s say we want to say that Id should always be greater than zero. For that we will have to start with calling RuleFor
method and then we can use the GreaterThan
validator as shown in the below line:
RuleFor(x => x.Id).GreaterThan(0);
Built-in vs Custom Validators
Fluent validation nuget package provides a lot of built-in validators which can be used for validating basic data types. We used one of the built-in validator, GreaterThan
, in the above example. Similar to this, there are validators for null check, empty strings, min length, max length, etc.
We can use these validators to implement the hypothetical validation requirements mentioned above. If you do not find the intended validator in list of built-in validators, then you can also write your own custom validator.
There is also a method Must
(and MustAsync
) which accepts a Func
as parameter. So basically, we can implement custom validation logic easily by using this. We can either create a separate method and call it from Must / MustAsync
validator or we can specify lambda expression.
Error Message
The built-in validators have default error messages. But if you do not want to use these default messages for any reason, there is a way to specify the custom error message by using WithMessage
method.
RuleFor(x => x.Id).GreaterThan(0).WithMessage(“Id is invalid.”);
Chaining of Validations
If needed, we can also chain more than one validators for a given property.
For example, the length of student name should not be more than 250 characters and it should not have less than 10 characters. This validation needs two different validators , minimum length validator and maximum length validator. These can be chained as shown in the line below:
RuleFor(x => x.Name).MinimumLength(10).MaximumLength(250);
StudentValidator Code
We have seen all the basic features which we are supposed to know before writing our first validator. Now, the code snippet given below shows the complete implementation of the validation rules which we were mentioned in previous section.
How to trigger the validation ?
Now we have a new endpoint which accepts a custom type, Student as input. We also have the validator for the custom type. But now the question is – how this validation logic would be triggered ?
For triggering the validation logic, we need to create the validator instance (either use new operator or we can use dependency injection here). On the instance of validator, Validate
or ValidateAsync
method can be called to trigger the validation.
It takes the object to be validated as input. It returns ValidationResult
object. This output object has a property IsValid
, indicating whether the object is valid. If this property is false, that means validation has failed and the object is invalid.
The ValidationResult also has a collection, Errors
. As the name suggests, this contains the list of errors which are identified by the validation logic. Each entry in Errors
collection is an instance of ValidationFailure
type. Each entry contains details about the each validation error.
The code given below uses these two properties. It is the same endpoint that we have created at the beginning of the article. It triggers the validation by using Validate
method. If the object is valid, then it is returned as it is , in the response. If the object is invalid, then error messages are selected from the error collection and they are returned in the response.
This is very primitive strategy to show the errors, just to demonstrate how the fluent validation works. In real world applications, the objects might be more complex and you may want to use different strategy to ensure that the caller exactly knows where the validation error has occurred.
I hope you find this information helpful. Let me know your thoughts.
instead of specifically using studentValidator.Validate(student); you can register validator and use it as Middleware in options while adding fluent validation in Program.cs This will eliminate use to use specific validator and you can use old if (!ModelState.IsValid) to validate model
builder.Services.AddControllers()
.AddFluentValidation(options =>
{
// Validate child properties and root collection elements
options.ImplicitlyValidateChildProperties = true;
options.ImplicitlyValidateRootCollectionElements = true;
// Automatic registration of validators in assembly
options.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly());
});