I’m using ASP, CQRS + MediatR and fluent validation. I want to implement user role validation, but I don’t want to mix it with business logic validation. Do you have any idea how to implement this?
I mean a specific validator must be executed for a specific request.
Something tells me the solution lies in IEnumerable< IValidator>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(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).ToArray();
if (failures.Any())
{
var errors = failures
.Select(x => new Error(x.ErrorMessage, x.ErrorCode))
.ToArray();
throw new ValidationException(errors);
}
}
return await next();
}
}
2
Answers
I see your concern, I also found myself in this situation. I wanted to separate my validators from handlers while also keeping them in the domain/business project. Also I didn’t want to throw exceptions just to handle bad request or any other custom business exception.
You have the right idea by
For this, you need to set up a mediator pipeline, so for every Command you can find the appropriate the appropriate validator, validate and decide whether to execute the command or return a failed result.
First, create an interface(although not necessary but it is how I did it) of
ICommand
like this:And,
ICommandHandler
like:This way we can only apply validation to commands. Instead of iheriting
IRequest<MyOutputDTO>
andIRequestHandler<MyCommand, MyOutputDTO>
you inherit fromICommand
andICommandHandler
.Now create a
ValidationBehaviour
for the mediator as we agreed before.This code simply, excepts all the validators in the constructor, because you register all your validator from assembly for your DI container to inject them.
It waits for all validations to validate async(because my validations mostly require calls to db itself such as getting user roles etc).
Then check for errors and return the error(here I have created a DTO to wrap my error and value to get consistent results).
If there were no errors simply let the handler do it’s work
return await next();
Now you have to register this pipeline behavior and all the validators.
I use autofac so I can do it easily by
If you use Microsoft DI, you can:
Example usage:
My generic DTO Wrapper
A sample Command:
Validator for it:
This way you always get a result object, you can check if
error is null
or!IsSuccess
and then create a customHandleResult(result)
method in your Controller base which can switch on the exception to returnBadReuqestObjectResult(result)
orForbiddenObjectResult(result)
.If you prefer to throw, catch and handle the exceptions in the pipeline or you wan’t non-async implementation, read this https://code-maze.com/cqrs-mediatr-fluentvalidation/
This way all your validations are very far from your handler while maintaining consistent results.
I think that your initial approach its right. When you say that you want to keep the auth validations apart from the other business validation, do you mean like returning a http error like 403 and 401 right?
If thats the case try marking the auth validations with and interface to identify they, and do not run all the validations at once. Search first in the collection for a validation with that interface, and if it fails send a custom exception that you can identity in a IActionFilter to set the wanted result. This code does not do that exactly but you can make an idea.