ASP.NET 核心中的流利验证
Fluent Validation in ASP.NET Core
我试图将我之前在 Asp.net MVC 4 上的项目中的验证替换为 Asp.net Core。并且有一些问题。 Asp.net 核心项目中的流程是这样的:
中间件 => ControllerCTOR => FluValidator => 过滤器 => 动作
此外,当 FluValidator 中的某些规则失败时,它只是 return 通过中间件堆栈向客户端响应错误。但我需要在 Filter 或 Action 中访问 ModelState。
为什么这不能正常工作?或者,如果它真的是正确的流程,如何让它更深入到行动?
启动
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(typeof(ValidateModelAttribute));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddNLog();
env.ConfigureNLog("nlog.config");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CorpLight API V1");
});
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseMiddleware<AuthenticateMiddleware>();
app.UseMvc();
}
中间件
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
验证者
public class CardInformationRequestValidator : AbstractValidator<RequestModel<CardInformationRequest>>
{
public CardInformationRequestValidator()
{
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.Currency)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.AccountNumber)
.NotNull()
.NotEmpty();
}
}
控制器
[Route("api/[controller]")]
[ApiController]
public class CardController : ControllerBase
{
private readonly ICardRepo _cardRepo;
private readonly IMapper _mapper;
public CardController(ICardRepo cardRepo, IMapper mapper)
{
_cardRepo = cardRepo;
_mapper = mapper;
}
[HttpPost]
public async Task<MessageWithElements<CardInformation, CardInfo>> CardInformations(RequestModel<CardInformationRequest> request)
{
if (!ModelState.IsValid)
throw new InvalidParametersException($"can't be empty");
//logic
}
}
过滤器
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
//logic
}
}
}
典型有效Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 1
}
}
典型无效Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 0
}
}
当货币不为零时有效,并到达过滤器。但是当它为零时,NotEmpty 失败并返回。
有效请求的典型响应:
{
"elements": [
{
<object fields>
}
],
"messageText": "string",
"messageNumber": 1
}
无效请求(400 错误请求)的典型响应:
{
"Request.Currency": [
"'Request. Currency' must not be empty."
]
}
即使模型无效,执行流程实际上也会到达 ValidateModelAttribute
和操作。但是有一个特定的情况,当 Request
属性 是 null
并且 CardInformationRequestValidator
在验证期间抛出异常。例如,当验证器尝试检查此规则时
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
它尝试获取 RU
属性 值但它抛出 NullReferenceException
因为 x.Request
是 null
。所以解决方案是更新验证逻辑
public CardInformationRequestValidator()
{
RuleFor(x => x.Request)
.NotNull()
.DependentRules(() =>
{
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.Currency)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.AccountNumber)
.NotNull()
.NotEmpty();
});
}
我找到了解决办法。问题出在过滤器中。由于 OnActionExecuting 方法请求永远不会到达。验证后,如果有任何失败的规则上下文直接进入 OnResultExecution 和 return 响应。
public class ValidateModelFilter : Attribute, IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (!context.ModelState.IsValid)
throw new InvalidParametersException(context.ModelState.StringErrors());
await next();
}
}
我试图将我之前在 Asp.net MVC 4 上的项目中的验证替换为 Asp.net Core。并且有一些问题。 Asp.net 核心项目中的流程是这样的:
中间件 => ControllerCTOR => FluValidator => 过滤器 => 动作
此外,当 FluValidator 中的某些规则失败时,它只是 return 通过中间件堆栈向客户端响应错误。但我需要在 Filter 或 Action 中访问 ModelState。
为什么这不能正常工作?或者,如果它真的是正确的流程,如何让它更深入到行动?
启动
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(typeof(ValidateModelAttribute));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddNLog();
env.ConfigureNLog("nlog.config");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CorpLight API V1");
});
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseMiddleware<AuthenticateMiddleware>();
app.UseMvc();
}
中间件
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
验证者
public class CardInformationRequestValidator : AbstractValidator<RequestModel<CardInformationRequest>>
{
public CardInformationRequestValidator()
{
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.Currency)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.AccountNumber)
.NotNull()
.NotEmpty();
}
}
控制器
[Route("api/[controller]")]
[ApiController]
public class CardController : ControllerBase
{
private readonly ICardRepo _cardRepo;
private readonly IMapper _mapper;
public CardController(ICardRepo cardRepo, IMapper mapper)
{
_cardRepo = cardRepo;
_mapper = mapper;
}
[HttpPost]
public async Task<MessageWithElements<CardInformation, CardInfo>> CardInformations(RequestModel<CardInformationRequest> request)
{
if (!ModelState.IsValid)
throw new InvalidParametersException($"can't be empty");
//logic
}
}
过滤器
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
//logic
}
}
}
典型有效Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 1
}
}
典型无效Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 0
}
}
当货币不为零时有效,并到达过滤器。但是当它为零时,NotEmpty 失败并返回。
有效请求的典型响应:
{
"elements": [
{
<object fields>
}
],
"messageText": "string",
"messageNumber": 1
}
无效请求(400 错误请求)的典型响应:
{
"Request.Currency": [
"'Request. Currency' must not be empty."
]
}
即使模型无效,执行流程实际上也会到达 ValidateModelAttribute
和操作。但是有一个特定的情况,当 Request
属性 是 null
并且 CardInformationRequestValidator
在验证期间抛出异常。例如,当验证器尝试检查此规则时
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
它尝试获取 RU
属性 值但它抛出 NullReferenceException
因为 x.Request
是 null
。所以解决方案是更新验证逻辑
public CardInformationRequestValidator()
{
RuleFor(x => x.Request)
.NotNull()
.DependentRules(() =>
{
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.Currency)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.AccountNumber)
.NotNull()
.NotEmpty();
});
}
我找到了解决办法。问题出在过滤器中。由于 OnActionExecuting 方法请求永远不会到达。验证后,如果有任何失败的规则上下文直接进入 OnResultExecution 和 return 响应。
public class ValidateModelFilter : Attribute, IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (!context.ModelState.IsValid)
throw new InvalidParametersException(context.ModelState.StringErrors());
await next();
}
}