【问题标题】:Fluent Validation in ASP.NET CoreASP.NET Core 中的流利验证
【发布时间】:2019-07-13 03:16:22
【问题描述】:

我尝试将我之前在 Asp.net MVC 4 上的项目中的验证替换为 Asp.net Core。并且有一些问题。 Asp.net Core 项目中的流程是这样的:

中间件 => ControllerCTOR => FluValidator => 过滤器 => 动作

此外,当 FluValidator 中的某些规则失败时,它只是通过中间件堆栈向客户端返回带有错误的响应。但我需要在 Filter 或 Action 中访问 ModelState。

为什么这不能正常工作?或者,如果它真的是正确的流程,如何让它更深入到Action?

启动

    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."
  ]
} 

【问题讨论】:

  • 请说明实际工作不正常的地方。可以访问 ModelState 还是其他东西?更清楚地描述问题,它太宽泛了。问候。
  • 例如我开始调试。如果发送有效请求,它将通过中间件到 ControllerCTOR,然后 Fluentvalidator 开始按规则检查模型。由于模型是有效的,ModelState 中的属性 IsValid 变为 true。请求进入过滤器,然后进入操作,我可以在其中检查 ModelState 是否有效并做出一些格式响应。但是如果模型无效。它只是通过失败的规则返回错误的响应,而不到达过滤器或操作。
  • 请在app.UseYourMiddleware的地方添加代码
  • 完成。添加启动的配置方法

标签: c# .net-core asp.net-core-2.0 fluentvalidation


【解决方案1】:

即使模型无效,执行流实际上也会到达ValidateModelAttribute 和操作。但有一种特殊情况,Request 属性为null 并且CardInformationRequestValidator 在验证期间抛出异常。例如,当验证器尝试检查此规则时

RuleFor(x => x.Request.RU)
    .NotNull()
    .NotEmpty();

它试图获取RU 属性值,但它抛出NullReferenceException,因为x.Requestnull。所以解决方法是更新验证逻辑

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

阅读更多in docson github

【讨论】:

  • 不。请求对象已填充。示例: { "request": { "ru": "string", "accountNumber": "string", "currency": 0 } } 当货币不为零时有效,并到达过滤器。但是当它为零时,NotEmpty 会失败并返回。我不知道为什么,如果验证器失败,可能某些配置会阻止进一步进行。
  • @RomanMakarenko 嗯,这很奇怪,因为它对我来说很好用。而且,如果您要发送 json,那么您在操作参数之前缺少 [FromBody],否则我相信它没有绑定。
  • @RomanMakarenko 另外,请在输入无效时添加您的输出。
  • 在问题文章中添加了响应正文。
  • 我也添加了 [FromBody] 并且没有任何变化。
【解决方案2】:

我找到了解决方案。问题出在过滤器中。由于 OnActionExecuting 方法请求永远无法到达。验证后,如果有任何失败的规则上下文直接转到 OnResultExecution 并返回响应。

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

【讨论】:

    猜你喜欢
    • 2020-03-03
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    • 2021-09-16
    • 1970-01-01
    相关资源
    最近更新 更多