Last active
June 27, 2022 22:28
-
-
Save DavidRogersDev/a9291deb6b333ac87434589c3e6e8a00 to your computer and use it in GitHub Desktop.
Gists for Medium Article - BusinessValidationPipeline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BusinessValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | |
where TResponse : class | |
where TRequest : IValidateable | |
{ | |
private readonly IValidator<TRequest> _compositeValidator; | |
private readonly ILogger<TRequest> _logger; | |
private readonly ICurrentUser _currentUser; | |
private readonly IActionContextAccessor _actionContextAccessor; | |
public BusinessValidationPipeline( | |
IValidator<TRequest> compositeValidator, | |
ILogger<TRequest> logger, | |
ICurrentUser currentUser, | |
IActionContextAccessor actionContextAccessor) | |
{ | |
_compositeValidator = compositeValidator; | |
_logger = logger; | |
_currentUser = currentUser; | |
_actionContextAccessor = actionContextAccessor; | |
} | |
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | |
{ | |
_logger.TraceBeforeValidatingMessage(); | |
var responseType = typeof(TResponse); | |
if (responseType.Name.StartsWith(nameof(ValidateableResponse), StringComparison.Ordinal)) | |
{ | |
var result = await _compositeValidator.ValidateAsync(request, cancellationToken); | |
if (!result.IsValid) | |
{ | |
var errorsCollated = result.ToDictionary(); | |
_logger.TraceMessageValidationFailed(errorsCollated, _currentUser?.UserName ?? GeneralPurpose.AnonymousUser); | |
// Add validation fail to ModelState to make it available there. Just in case it is needed. | |
result.AddToModelState(_actionContextAccessor.ActionContext.ModelState, string.Empty); | |
// Deal with type depending on whether it is the generic version of ValidateableResponse or not. | |
var resultType = responseType.GetGenericArguments().FirstOrDefault(); | |
if (ReferenceEquals(resultType, null)) | |
{ | |
var nonGenericInvalidResponse = | |
Activator.CreateInstance( | |
responseType, | |
errorsCollated | |
) as TResponse; | |
return nonGenericInvalidResponse; | |
} | |
var invalidResponseType = typeof(ValidateableResponse<>).MakeGenericType(resultType); | |
var invalidResponse = | |
Activator.CreateInstance( | |
invalidResponseType, | |
null, | |
errorsCollated | |
) as TResponse; | |
return invalidResponse; | |
} | |
_logger.TraceMessageValidationPassed(); | |
return await next(); | |
} | |
throw new Exception($"IValidateable implementation must be a {nameof(ValidateableResponse)}."); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment