问题描述
我正在使用 FluentValidation 开发 .NET Core 3.0 MVC 项目,以验证来自 React 前端的输入。我不能指望输入在前端得到验证。
我正在使用 InvalidModelStateResponseFactory 来捕获转换错误,例如"zz" 为整数,并使用 FluentValidation 验证数字是否有效,例如
我的问题是,当 FluentValidation 捕获错误时,即使没有转换错误,InvalidModelStateResponseFactory 也会随后捕获它。并且 FluentValidation 的错误信息丢失了。
FluentValidation 可以验证 int 是否过大。例如:
public class XCommand
{
public int myInt {get;set;}
}
和验证器:
public class XCommandValidator : AbstractValidator<XCommand>
{
RuleFor(x => x.myInt).InclusiveBetween(0,99).WithMessage("only below 100");
}
如果验证器失败 {myInt : 100},我可以捕获异常并返回一个很好的错误响应。
如果前端发送 {myInt : "zz"} 我得到一个由 MVC 生成的错误(不涉及 InvalidModelStateResponseFactory):
{"errors":{"myInt":["无法将字符串转换为整数:zz。路径 'myInt',第 1 行,位置 1。"]},"type":"https:/ /tools.ietf.org/html/rfc7231#section-6.5.1","title":"发生了一个或多个验证错误。","status":400,"traceId":"|ae6d5a28-4822b30b8c0ae039。"}
当我遇到转换错误时,我想控制我发送给前端的错误响应的格式。为了捕捉转换错误,我在 Startup.cs 中设置了一个 InvalidModelStateResponseFactory:
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = contex =>
{
// create a nice error response
};
});
我的问题是,如果我没有转换错误,而是一个无效的值,例如{myInt : 100} 并且在 FluentValidator 中失败。激活 InvalidModelStateResponseFactory 并将 InvalidModelStateResponseFactory 中生成的错误响应发送到前端。 FluentValidation 中生成的消息丢失。
如何避免 InvalidModelStateResponseFactory 干扰已在 FluentValidation 中生成的错误响应?
解决方法
解决方案是创建一个自定义验证过滤器,并捕获来自 MVC“本机”和 FluentValidator 的所有验证错误。
public class CustomValidationFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context) { }
public void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
SortedList<string,List<string>> err = new SortedList<string,List<string>>();
foreach (var modelStateKey in context.ModelState.Keys)
{
var modelStateVal = context.ModelState[modelStateKey];
if (modelStateVal.ValidationState == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Invalid)
{
err.Add(modelStateKey,modelStateVal.Errors.Select(e => e.ErrorMessage).ToList());
}
}
var responseObj = new
{
Errors = err
};
context.Result = new JsonResult(responseObj)
{
StatusCode = 400
};
}
}
}
在 StartUp.ConfigureServices 中,必须添加:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});