← All articles

Validation in a MediatR pipeline

A MediatR validation behaviour avoids repeating the same input checks across handlers. It establishes a clear boundary before business execution.

t.Turgay
Validation in a MediatR pipeline

When every command handler starts with the same field checks, a shared request boundary may be a better home for them. A MediatR pipeline behavior wraps the handler. For create, update and publication flows in my archive, that boundary makes the handler's actual business decision easier to read.

next is the boundary into the handler

This fragment uses the MediatR 12.x delegate shape and FluentValidation. Match signatures and registration APIs to the installed version; the article is not a recommendation to upgrade to an unspecified newest package. A behavior class is not a complete API or persistence workflow.

using FluentValidation;
using MediatR;
public sealed class ValidationBehavior<TRequest,TResponse>(
    IEnumerable<IValidator<TRequest>> validators):IPipelineBehavior<TRequest,TResponse>
    where TRequest:notnull
{
    public async Task<TResponse> Handle(TRequest request,RequestHandlerDelegate<TResponse> next,CancellationToken token)
    {
        var errors=new List<FluentValidation.Results.ValidationFailure>();
        foreach(var validator in validators)
        {
            var result=await validator.ValidateAsync(new ValidationContext<TRequest>(request),token);
            errors.AddRange(result.Errors);
        }
        if(errors.Count>0)throw new ValidationException(errors);
        return await next();
    }
}

Validators registered for TRequest are resolved, run through ValidateAsync and contribute their failures to one collection. Any failure prevents next from running, so the handler is not invoked. With no validators the loop is empty and execution continues normally.

Sequential execution can be intentional

Validators may share a scoped DbContext or another dependency that does not support concurrent operations. Replacing the loop with Task.WhenAll is not automatically safe. Parallel validation needs evidence that dependencies are independent and resource usage is acceptable, not just a shorter implementation.

The cancellation token is passed into each validation call and should continue into any underlying asynchronous operation. A growing collection of expensive data queries can also indicate that business orchestration has leaked into field validation.

Register the behavior in the real host

Adding the class does not intercept requests by itself. Register handlers and the open generic behavior using the selected MediatR version's API. For the 12.x shape, a registration fragment can look like this alongside the actual validator registrations:

services.AddMediatR(config =>
{
    config.RegisterServicesFromAssemblyContaining<Program>();
    config.AddOpenBehavior(typeof(ValidationBehavior<,>));
});

This does not automatically invent validator instances; they need DI registration too. A marker type from the handler assembly can replace Program where appropriate. Package version, licensing and maintenance remain explicit dependency decisions.

Translate validation failure at the transport boundary

The example uses ValidationException. Returning an expected ServiceResult is another application design, and the two should not be mixed accidentally for the same condition. If exceptions are the pipeline contract, API middleware must translate these failures into a controlled 400 response with field errors, not an exposed stack trace or misleading 500.

The same request can be sent outside HTTP, for example from a worker or a test. The behavior should therefore not create IActionResult or depend on the web host.

Consider ordering with authorization and transactions

Goals such as rejecting unauthorized requests before sensitive queries and running cheap field checks before a write transaction affect pipeline order. Understand which behavior wraps which operation rather than assuming one universal order. State-dependent rules such as whether a record can be published still belong in the handler or service, with appropriate transaction protection.

Verify that invalid input invokes the handler zero times and valid input exactly once. Include aggregated validator failures, no validators and cancellation. Those tests establish that the behavior participates in the real request flow rather than merely compiling as an unused class.

MediatR behaviors; Validator dependency registration.

✳ You have reached the end of this orbit.
Explore more articles →
turgay.A little code, a few pixels, a lot of curiosity.© 2026 · Made on Earth ✳