'Return response with errors instead of throwing exception in validation pipeline mediatr 3
I am currently working with Pipeline behavior in Mediatr 3 for request validation. All the examples that I came across were throwing ValidationException if any failures happening, instead of doing that I want to return the response with the error. Anyone has idea on how to do it?
Below is the code for the validation pipeline:
public class ValidationPipeline<TRequest, TResponse> :
IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationPipeline(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
{
throw new ValidationException(failures);
}
return next();
}
}
Note: I found this question Handling errors/exceptions in a mediator pipeline using CQRS? and I am interested in the 1st option on the answer, but no clear example on how to do that.
This is my response class:
public class ResponseBase : ValidationResult
{
public ResponseBase() : base() { }
public ResponseBase(IEnumerable<ValidationFailure> failures) : base(failures) {
}
}
and I added below signature in the validation pipeline class:
public class ValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
where TResponse : ResponseBase
I did this then in the Handle method:
var response = new ResponseBase(failures);
return Task.FromResult<TResponse>(response);
But that gave me error 'cannot convert to TResponse'.
Solution 1:[1]
Simply don't call next
if there's any failures:
public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
{
var response = new Thing(); //obviously a type conforming to TResponse
response.Failures = failures; //I'm making an assumption on the property name here.
return Task.FromResult(response);
}
else
{
return next();
}
}
Note:
Your class (Thing
in my example) must be of type TResponse
Solution 2:[2]
Several years ago, I created general Result object, which I am constantly improving. It is quite simple, check https://github.com/martinbrabec/mbtools.
If you will be ok with the Result (or Result<>) being the return type every method in Application layer, then you can use the ValidationBehavior like this:
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
where TResponse : Result, new()
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
if (_validators.Any())
{
var context = new ValidationContext(request);
List<ValidationFailure> failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
{
TResponse response = new TResponse();
response.Set(ErrorType.NotValid, failures.Select(s => s.ErrorMessage), null);
return Task.FromResult<TResponse>(response);
}
else
{
return next();
}
}
return next();
}
}
Since all your handlers return Result (or Result<>, which is based upon Result), you will be able to handle all validation errors without any exception.
Solution 3:[3]
You can configure validation handling using package https://www.nuget.org/packages/MediatR.Extensions.FluentValidation.AspNetCore
Just insert in configuration section:
services.AddFluentValidation(new[] {typeof(GenerateInvoiceHandler).GetTypeInfo().Assembly});
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 | |
Solution 2 | Harold Trotter |
Solution 3 | GetoX |