Using asynchronous fluent validations in ASP.NET API

Cheranga Hatangala
Cheranga
Published in
4 min readAug 9, 2022

--

Api — Free seo and web icons (flaticon.com)

Context

While at work, my friend Ben, and myself came across an interesting problem to fix, when upgrading our APIs to latest .NET and upgrading their respective packages.

Validation is crucial when designing APIs, and we are using the wonderful FluentValidation.AspNetCore in our APIs. How it seamlessly allow the engineers to define the validations, and integrate with the ASP.NET echo system is a breeze.

Some of our validators contain asynchronous validations, and after upgrading we got the below error.

The error clearly states that our validators contain asynchronous validations, and the ASP.NET validation pipeline is not asynchronous and hence can’t invoke asynchronous rules.

Yes, it can be debated that validations must be synchronous, but in reality there are many situations where certain validations have to be asynchronous, such as validating with a data store entry or with a web service. Also it can be the opinion, that such actions should be the responsibility of the core layer(application / domain), but this was existing code, and in our opinion the approach was correct ☺️

The code for this article can be found in GitHub

Options Considered

  • Move the async validations from the validators, and move it to the application or the business layer.

Then we will have to create another level of abstraction to be injected into the controller or the core layers, and to make changes to our existing validators and respective classes. Also we’ll lose all the “async” validations performed at the time of model binding.

  • Implementing a custom action filter.

This action filter needs to perform asynchronous operations, and should be able to use the existing validators without any code changes to them. At the time of execution it needs to be able to find the respective IValidator
and perform the validations, and most importantly if it contains validation errors to short circuit the ASP.NET pipeline.

💡 we chose to implement a custom action filter to perform validations.

Demonstrating the problem, with a sample API

This is an API to manage products. For the sake of brevity I have created only a single endpoint which simulates an adding of a product.

In the Program.cs we register the dependencies.

The Validator for the DTO

The action method which will expose the endpoint

Note that I have used another Nuget package called HybridModelBinding to bind the model from different model binders and value providers.
As you can see below the DTO is getting populated by both from the header and from the body. Hybrid model binding is not relevant to the validations, but wanted to demonstrate that since the validation approach must happen after the model binding.

When you run this project, and make an HTTP POST request to this endpoint, you’ll get the above mentioned error.

Custom action filter to validate DTOs

When designing the solution, we wanted to design, and implement the filter to be used with our many other APIs.
So we could make this as a common library such as a nuget package to be used.

Let’s explore what has been implemented in the action filter.

  • Inject an ICustomValidatorFactory which will get the respective IValidator instance.
  • Implement the IAsyncActionFilter interface and it’s OnActionExecutionAsync method.

In here for each action argument’s type it tries to get the respective IValidator instance if available. If a validator instance is available it will call it’s ValidateAsync method. The ValidateAsync method will perform both synchronous and asynchronous validations. Finally if there are validation errors it will use these validation errors to create a ProblemDetails response and set it in the action result and, short circuit the ASP.NET pipeline.

Please see the extension method to return ProblemDetails from ValidationFailures.

Most importantly let’s check the CustomValidatorFactory implementation.

Let’s explore the validator factory now.

  • An IServiceProvider is injected, this will be injected by ASP.NET itself.
  • We create an IServiceScope using the IServiceProvider because we wanted this code to be independent of how the actual IValidator instances are registered with the dependency injection for their lifetime.
  • Create an IValidator instance and return it.

Let’s put these all together

  • Extension method to register FluentValidators, and the CustomValidatorFactory

Notice that there are two options which can be used, one with a single assembly and the other with a set of assemblies where the validators are located.

In Program.cs let’s use it. Note that the ApiValidationFilter has been registered in the controller configuration.

Testing

Please find the unit tests for this in the Demo.Products.Api.Tests project.

If you would like to run this project, and test I highly recommend the RestClient either in VSCode or in Rider. I am using Rider, and you can see the results below.

--

--