Получить код ошибки из FluentValidator в ActionFilter - PullRequest
0 голосов
/ 13 февраля 2019

Я использую библиотеку FluentValidation для автоматической проверки моделей, которая работает нормально, однако существует требование установить код ошибки с использованием метода WithErrorCode() в валидаторе (AbstractValidator<T>).Это также хорошо работает, проблема заключается в получении этого кода из основного действия фильтра ASP.NET MVC, определенного следующим образом:

public class ActionModelValidationAttribute : ActionFilterAttribute
{
    readonly ILogger<ActionModelValidationAttribute> log;
    public ActionModelValidationAttribute (ILogger<ActionModelValidationAttribute> log) => this.log = log;

    public override void OnActionExecuting (ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var routeName = context.RouteData.Values["action"] ?? "unknown";
            log.LogDebug($"model validation failed for {routeName}");

            var errors = context.ModelState.Values.Where(state => state.Errors.Count > 0)
                .SelectMany(errs => errs.Errors)
                .Select(e => new BaseErrorResponse(){
                    Code = 404, // <<-- this is where I would like the code from WithErrorCode()
                    Details = e.Exception?.Message ?? "",
                    Message = e.ErrorMessage,
                    Field = "field"
                }).ToList();

            var response = new ValidationErrorResponseModel()
            {
                Message = "Bad Request",
                Errors = errors                    
            };

            context.Result = new JsonResult(response)
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
    }
}

Тип ошибок: Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateEntry

Тип e: Microsoft.AspNetCore.Mvc.ModelBinding.ModelError

Вот мой валидатор:

public class ViewModelValidator : AbstractValidator<ViewModel>
{
    public ViewModelValidator() { 
        RuleFor(m => m.DistributorId)
            .NotNull().WithErrorCode("910000")
            .NotEmpty().WithErrorCode("910001");
    }
}

1 Ответ

0 голосов
/ 15 февраля 2019

Не похоже, что библиотека FluentValidation справится с этим самостоятельно.Обходной путь - реализовать интерфейс IValidatorInterceptor в конкретной реализации AbstractValidator<T>.Кэш памяти может использоваться для хранения уникального идентификатора запроса, который затем позволяет извлечь идентификатор из кэша из фильтра действий.Будет возвращен объект ValidationResult, который содержит всю расширенную информацию проверки.

Ниже приведены примеры кода:

public abstract class BaseModelValidator<T> : AbstractValidator<T>, IValidatorInterceptor
{
    protected readonly IMemoryCache cache;
    protected readonly ILogger<BaseModelValidator<T>> log;
    protected string RequestId { get; set; }

    public BaseModelValidator(IMemoryCache cache, ILogger<BaseModelValidator<T>> log)
    {
        this.cache = cache;
        this.log = log;
    }

    public virtual ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
    {
        RequestId = controllerContext.HttpContext.TraceIdentifier;
        return validationContext;
    }

    public virtual ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result)
    {
        cache.Set(RequestId, result, TimeSpan.FromMinutes(1));
        return result;
    }
}

Фильтр глобального действия:

public class ActionModelValidationAttribute : ActionFilterAttribute
{
    readonly ILogger<ActionModelValidationAttribute> log;
    readonly IMemoryCache cache;
    public ActionModelValidationAttribute(IMemoryCache cache, ILogger<ActionModelValidationAttribute> log) 
    {
        this.log = log;
        this.cache = cache;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var key = context.HttpContext.TraceIdentifier;
            cache.TryGetValue<ValidationResult>(key, out var result);

            if (result == null) ReturnError(context, key); // impl ReturnError however you like

            cache.Remove(key);
            var count = result.Errors.Count();
            var controllerName = context.RouteData.Values["Controller"] ?? "unknown";
            var routeName = context.RouteData.Values["Action"] ?? "unknown";
            var response = result.AsBaseResponse();
            log.LogDebug($"Model validation failed. {count} errors in model for {controllerName}.{routeName}");

            context.Result = new JsonResult(response)
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...