Every web application or micro-service that you build will have some form of validation. I've been using the standard validation attributes in ASP.NET core for ages. And I've also been cursing at them for ages. Luckily, there's a better way!
In this post we're going to explore FluentValidation. A validation library that you can use in .NET Core and .NET 5 for validating the input for your application.
We'll cover the following topics:
- Installing FluentValidation in your web app
- Building a basic validator
- Advanced validation scenarios
Let's get started by installing FluentValidation.
Installing FluentValidation in your web app
Although FluentValidation works with every type of .NET project, We're going to focus on using it in ASP.NET Core.
FluentValidation has a number of different packages, here are the most important ones you should know about:
- FluentValidation - The core package that you can use anywhere
- FluentValidation.AspNetCore - The package for ASP.NET Core
You can install FluentValidation as a NuGet package in your project. There's different options for installing nuget packages. For this post we'll use the command-line tooling of .NET.
Execute the following command in the folder where your ASP.NET Core project is located:
After installing the package, we have to open up the Startup.cs file and add the following code to the ConfigureServices
method:
This registers the controllers functionality in our application and sets up the fluent validation logic. We're using RegisterValidatorsFromAssembly
to automatically register any validators that we may have in the application.
Note: Although we're using AddControllers
, the same registration of fluent validation also works in combination with AddRazorPages
and AddControllersWithViews
.
Now that we have set up FluentValidation, let's take a look at building a basic validator.
Building a basic validator
FluentValidation is based on a class called the Validator. The Validator class encapsulates the validation logic of a single object that we want to validate.
Before we can start building a validator, we need to have something to validate. Let's build a basic controller with some input that we need to validate.
The controller has a method CreateAsync
that accepts a CreateCakeCommand
. Before doing anything with the input, it makes sure that the ModelState
is valid. If it's invalid we'll let the user know. Otherwise, we're creating the new cake in the catalog.
We're assuming here that validation happens automatically. This is the case for validation attributes that come with ASP.NET Core. However, since we've registered FluentValidation, the same is true for the validators introduced by FluentValidation.
Let's take a look at a basic validator for the CreateCakeCommand
.
A validator derives from the AbstractValidator<T>
class. It needs a generic argument which is the class that you want to validate: CreateCakeCommand
.
In the constructor of the class we can specify one or more rules for properties. As an example we've setup rules for the Name
and Description
property.
Note: There are a number of default rules available. You can find them in the documentation: https://docs.fluentvalidation.net/en/latest/built-in-validators.html
Once we have your validator class ready, we're done. When data comes into the application, the validator is automatically called and the results are mapped to the ModelState
property.
Now that we have basic validation set up, let's take a look at some of the more advanced validation scenarios that you may run into.
Advanced validation scenarios
There are quite a few interesting scenarios that FluentValidation supports. Here are a couple of interesting ones:
- Custom validations that require a database or service
- Validation rules that depend on other validation rules
- Validation of child collections
Let's take a look at each of these scenarios and discover how they're handled.
Custom validations that require a database or service
Sometimes we want to make sure that a particular value in the input is or isn't in the database. For example, if we let the user specify a category for a cake in the sample application, we want to make sure that the category exists.
Let's extend the validator to support this:
Instead of using one of the standard rules, we're specifying a custom validation method. This method accepts the value of the property and returns a boolean.
We can now use other components such as a ICategoryRepository
to call into our database to make sure that the data is valid.
But what if we wanted to make sure that the categoryId is at least higher than zero before we call into the database? We can do that too, using dependent validation rules.
Validation rules that depend on other validation rules
We can make validation rules depend on other validation rules in two ways. First, we can use the DependendRules
:
Notice how we changed the rules for CategoryId
so that we first validate that the value is greater than zero. If that's the case, we can then validate that it exists.
This works great for basic scenarios, but it gets pretty messy if you have more rules that you need to cascade.
We can solve this problem in another way, using the When
method. We can use the When
method to specify a condition for a validation rule.
We end up with a much cleaner Validator by using conditions on validation rules.
Note: Personally, I think it's fine to use the dependent rules in scenarios that aren't too complicated. The use of When
may sometimes be even less unreadable. The meaning of dependent rules can be quite different from the conditional When
.
Now that we've seen how to chain rules and make them conditional, let's see what we can do for validating child collections.
Validation of child collections
Sometimes we have a collection of child elements in an input class. We can validate these too using child rules or a separate validator. Let's start by looking at using child rules.
As an example we'll modify the input command so that it has a collection of ingredients. We're assuming that an ingredient has a relative weight and a name.
We need to extend the validator in order for us to validate the new ingredients collection like so:
Note the new RuleForEach
method call in the validator. We tell the validator to check the Ingredients
property using a set of rules. We then call the ChildRules
method to specify the rules for each ingredient.
While this is great for some basic set of rules, it can be quite tricky to make this work for a more complex scenario.
Instead of using ChildRules
we can also call SetValidator
which allows us to specify a separate validator class that is focused on the type of object that is contained in the collection.
The validator that we specify in the SetValidator
method is implemented in the same way as the validator of the root object.
Depending on the scenario you may want to choose ChildRules
over the SetValidator
technique. I personally think the former is easier for smaller objects, while the latter is easier with more complex objects.
There's a lot more to cover in the library, but I think you'll get the gist of what this library is capable of.
Summary and resources
In this article we've covered how to set up FluentValidation in ASP.NET Core and how to build a basic validator. We then looked at building more complicated validators using custom validation rules, conditional rules, and collection rules.
If you're interested in learning more, I can highly recommend reading the documentation. It contains a very detailed explanation of what you can do with FluentValidation.
Have fun!