Validation with Mediator Pipelines in ASP.NET Core Applications

Validate commands, events or queries with transversal behavior

João Simões
Nov 4, 2020 · 4 min read

When implementing web applications that manipulate information, there is always the need to validate data sent by the clients, ensuring business rules are properly implemented.

Previously I talked about the implementation of Command Query Responsibility Segregation (CQRS) and Event Sourcing (ES) using a mediator pattern and how to support transversal behavior via pipelines.

In this article I’m going to demonstrate how validation can be enforced into your commands and events before reaching the handlers, making sure that invalid data will be rejected by the API.

This approach not only reduces the amount of duplicated code, it also ensures validations won’t be forgotten, unless explicitly stated.

The project

To make it faster to implement the validation pipeline, I’m going to leverage this example on both of my previous articles, in which we implemented an endpoint to manage products and introduced both a logging and timeout pipelines:

The source code is available on GitHub.

The validations

To help us configure the rules we are going to use a very popular and one of my favorites libraries FluentValidation by creating classes implementing the interface IValidator<T>.

These classes will be added to the dependency injection container and used by the pipeline to validate both the commands and events before reaching the handler.

Lets start by installing the NuGet FluentValidation:

Create a Validations folder at the project root level, with a subfolder Products.

Inside the Products folder create both a validator for CreateProductCommand and CreatedProductEvent by extending the class AbstractValidator<T> (which itself implementes the interface IValidator<T>) and configure some rules in the constructors:

Feel free to create validators for all other commands and events, I’m just focusing on these for simplicity. You can also check the documentation for supported rules and detailed usage instructions.

Open the Startup.cs file and register all classes implementing IValidator<T> into the container:

The pipeline

Because for this example we are going to enforce validation only on commands and events, since they either mutate or represent the system state at a given point in time, the pipeline will be implemented as follows:

  1. Intercept any command or event;
  2. Resolve a required instance of IValidator<TCommand> or IValidator<TEvent> from the container;
  3. Invoke the method ValidateAndThrowAsync, failing with a ValidationException if something is invalid;

Inside the Pipelines folder create a ValidationPipeline class extending Pipeline (because we only need some of the methods):

Open the Startup.cs file and add the ValidationPipeline at least after the LoggingPipeline ensuring, in case invalid data is submitted, we can still see it in the logs:

services.AddMediator(o =>
{
o.AddPipeline<LoggingPipeline>();
o.AddPipeline<TimeoutPipeline>();
o.AddPipeline<ValidationPipeline();
o.AddHandlersFromAssemblyOf<Startup>();
});

If you now start the server and try to create a product with invalid data you will receive a ValidationException.

Because returning an HTTP 500 due to invalid data would be confusing to the client, lets just finish this example by creating an ASP.NET Core middleware converting this exception into a more appropriate code, like HTTP 422.

Once again, open the Startup.cs file and register the middleware immediately after the developer exception page, catching this exception and returning HTTP 422 with a more detailed JSON representation:

Submit invalid data again and a more detailed message should be returned:

Speeding things up!

If just like me, you can see yourself using a pipeline for validation in most of your projects, there is already a pipeline available via NuGet that should be configurable to most use cases while also providing a simpler way to register the validators into the container:

Install SimpleSoft.Mediator.Microsoft.Extensions.ValidationPipeline via NuGet:

Use the extension method AddPipelineForValidation and enforce both command and event validations and use the extension method AddValidatorsFromAssemblyOf to scan for all IValidator<T> classes and register them into the container.

For the current project, the ConfigureServices method would be similar to the following:

Conclusion

I hope this article gave you a good idea on how to use mediator pipelines to ensure that all your commands, events and even queries are initialized with proper data, either implementing your own pipeline or by using the existing ValidationPipeline NuGet.

I also made an article about transaction management with pipelines, you may also find it helpful:

The Startup

Get smarter at building your thing. Join The Startup’s +794K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

João Simões

Written by

Solutions Architect trying to solve world “problems”!

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +794K followers.

João Simões

Written by

Solutions Architect trying to solve world “problems”!

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +794K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store