我正在使用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");
}
}
答案 0 :(得分:0)
似乎FluentValidation库本身不能解决这个问题。一种解决方法是在IValidatorInterceptor
具体实现上实现AbstractValidator<T>
接口。内存缓存可用于存储唯一的请求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
};
}
}
}