Mediator, Fluent Validation & OneOf, как сделать pipeline?

.net 7 web api, MediatoR 12, FluentValidation & OneOf, не могу сообразить как сделать PipelineBehavior, чтобы из него не надо было кидать исключение, если запрос не прошел проверку в пайплайне, а можно было вернуть OneOf, где в ApiError будут лежать ошибки валидации.
Wrappers:
public interface IMyRequest<TResponse> : IRequest<OneOf<ApiError, TResponse>>
{

}

public interface IMyRequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, OneOf<ApiError, TResponse>>
    where TRequest : IMyRequest<TResponse>
{

}


Pipeline:
internal sealed class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
    where TResponse : IOneOf, new()


Но таким образом никак нельзя вернуть OneOf, упорно пытается сказать, что не может преобразовать OneOf в TResponse:
return OneOf<ApiError, TResponse>.FromT0(new ApiError { Text = "Test" });


Как можно победить это всё? Сделать 1 общий объект, в котором будет лежать и результат, и ошибки (если нет, то null, null не отдавать клиенту в json'e)? Но хотелось бы иметь функционал как у OneOf, чтобы метчить результаты на IActionResult (изобретать свой велосипед?).
  • Вопрос задан
  • 208 просмотров
Пригласить эксперта
Ответы на вопрос 1
petermzg
@petermzg
Самый лучший программист
Архитектурно не нужно отдавать ApiError.
У web приложения должны быть стандартизированны ошибки, а значит их формат должен генериться в единой точке кода.
Поэтому нужно просто обрабатывать результат валидации от FluentValidation. Примерно так:
public sealed class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : class, IRequest<TResponse>
    {
        private readonly IEnumerable<IValidator<TRequest>> validators;

        public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
        {
            this.validators = validators;
        }

        public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            if (!validators.Any())
            {
                return await next();
            }

            var errors = new List<ValidationFailure>();
            var context = new ValidationContext<TRequest>(request);
            foreach (var validator in validators)
            {
                var validationResult = await validator.ValidateAsync(context);
                if (validationResult.Errors != null && validationResult.Errors.Count > 0)
                    errors.AddRange(validationResult.Errors);
            }

            if (errors.Any())
            {
                throw new ValidationException(errors);
            }
            return await next();
        }
    }

Что преобразует результат валидации в ваш Exception (ValidationException).
Который вы уже просто перехватываете в ExceptionFilter, что реализован на примере:
public class ApiExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
   // Тут отдаете уже стандартизированный ответ на варианты ошибок с правильно прописанным Status Code 
}
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы