'FluentValidation inside a MediatR pipeline and Razor Pages

I'm working on a project where everything is done through a mediatR pipeline.

To make things easy the pipeline is this : -> Validation -> Handler

The Validation layer is using FluentValidation and throws a ValidationException if something is wrong.

public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        if (_validators.Any())
        {
            var context = new ValidationContext<TRequest>(request);

            var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
            var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();

            if (failures.Count != 0)
                throw new ValidationException(failures);
        }

        return await next();
    }
}

Everything is working fine up to now.

Now, I have my page handler like this :

[BindProperty]
public LoginCommand Data { get; set; }

public async Task<IActionResult> OnPostAsync(CancellationToken cancellationToken)
{
    try
    {
        await _mediator.Send(Data, cancellationToken);
        return LocalRedirect("/Index");
    }
    catch (ValidationException ex)
    {
        var res = new ValidationResult(ex.Errors);
        res.AddToModelState(ModelState, nameof(Data));
        return Page();
    }
}

Doing things like this works fine, the drawback is that it makes a lot of code for every page handler.

Is there a way to simply this so the handler would look like this :

[BindProperty]
public LoginCommand Data { get; set; }

public async Task<IActionResult> OnPostAsync(CancellationToken cancellationToken)
{
    await _mediator.Send(Data, cancellationToken);
    return LocalRedirect("/Index");
}

and have this part of the code everywhere like a filter / middleware

    try
    {
        // HANDLER CODE
    }
    catch (ValidationException ex)
    {
        var res = new ValidationResult(ex.Errors);
        res.AddToModelState(ModelState, nameof(VIEWMODEL_NAME));
        return Page();
    }


Solution 1:[1]

You could add a ValidationExceptionPipelineBehavior before the ValidationPipelineBehavior in the MediatR pipeline.

That would ensure all ValidationExceptions are caught.

Having an ExeptionPipelineBehavior is fairly common.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MatthiasA