【问题标题】:Retrieve ErrorCode from FluentValidator in ActionFilter从动作过滤器中的 FluentValidation 中检索错误代码
【发布时间】:2019-02-15 05:29:27
【问题描述】:

我正在使用 FluentValidation 库来自动验证运行良好的模型 - 但是 - 需要使用验证器 (AbstractValidator<T>) 中的 WithErrorCode() 方法设置错误代码。这也可以正常工作,然后问题是从这样定义的 ASP.NET MVC Core Action Filter 中检索该代码:

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
            };
        }
    }
}

errs 的类型是 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");
    }
}

【问题讨论】:

    标签: asp.net-core fluentvalidation


    【解决方案1】:

    FluentValidation 库似乎无法自行处理此问题。一种解决方法是在AbstractValidator&lt;T&gt; 具体实现上实现IValidatorInterceptor 接口。内存缓存可用于存储唯一的请求 ID,然后可以从操作过滤器内的缓存中检索 ID。将返回一个 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
                };
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-20
      • 2011-11-17
      • 2015-07-23
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 2021-01-28
      • 1970-01-01
      相关资源
      最近更新 更多