当模型的流畅验证在 ASP.Net Core 中无效时,为什么我的控制器会被命中?

Why is my controller hit when the fluentvalidation for the model is not valid in ASP.Net Core?

我相信我正在根据文档做所有事情。我在 Startup.cs class:

中设置了流畅的验证
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSwaggerGen(swagger =>
            {
                swagger.SwaggerDoc("v1", new OpenApiInfo { Title = _swaggerApiName });
                swagger.DescribeAllParametersInCamelCase();
            });

            services.AddMvcCore()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.IgnoreNullValues = false;
                })
                .AddFluentValidation(options =>
                {                    
                    options.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
                    options.RegisterValidatorsFromAssemblyContaining(typeof(ContentCoreMarker));
                    options.ImplicitlyValidateChildProperties = true;
                })
               .AddApiExplorer()
               .AddRazorViewEngine();
        }

我的简单验证器。请注意,在 RuleFor 上遇到了一个断点。

    public class AddSectionRequestValidator : AbstractValidator<AddSectionRequest>
    {
        public AddSectionRequestValidator()
        {
            RuleFor(m => m.SectionName)
                .NotEmpty()
                .MinimumLength(1)
                .WithMessage("invalid");

            RuleFor(m => m.ParentSectionId)
                .NotEmpty();
        }
    }

fluentvalidation 不应该自动 return 验证错误而不触发控制器操作吗?

根据 documentation:

If you want to disable this behaviour so that FluentValidation is the only validation library that executes, you can set the RunDefaultMvcValidationAfterFluentValidationExecutes to false in your application startup routine

Controller 中的断点仍然命中。我还创建了验证器的本地副本进行测试,结果是模型无效。

        [HttpPost]
        public async Task<IActionResult> Post([FromBody]AddSectionRequest request)
        {
            var validator = new AddSectionRequestValidator();
            var isValid = validator.Validate(request); // Not valid

            var result = await _addSectionRequestHandler.Handle(request);
            return Ok(result.NewSectionId);
        }

我正在使用 ASP.Net Core 3.1

你的控制器被击中了,因为这是你应该处理验证的地方。 documentation 有这个在控制器中使用验证器的例子:

Once you’ve configured FluentValidation, ASP.NET will then automatically validate incoming requests using the validator mappings configured in your startup routine.

[HttpPost]
public IActionResult Create(Person person) {

    if(!ModelState.IsValid) { // re-render the view when validation failed.
        return View("Create", person);
    }

    Save(person); //Save the person to the database, or some other logic

    TempData["notice"] = "Person successfully created";
    return RedirectToAction("Index");

}

如您所见,他们会检查 ModelState 是否有效,然后 return 使用 Person 模型查看视图。

由于您没有 return 视图我建议您尝试 return 模型状态:

[HttpPost]
public async Task<IActionResult> Post([FromBody]AddSectionRequest request)
{
    if(!ModelState.IsValid) return BadRequest(ModelState);

    var result = await _addSectionRequestHandler.Handle(request);
    return Ok(result.NewSectionId);
}

这会 return 一个 json 像这样:

{
  "Name": ["The Name field is required."]
}

我还发现这个 blogpost 更详细并封装了模型验证,以便为每个控制器操作启用它。

终于找到问题了。我遇到的问题是我的控制器顶部缺少 [ApiController] 属性:

    [ApiController] // <-- Fix is here
    [Route("[controller]")]
    public class SectionController : ControllerBase
    {

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] DtoArticle request)
        {
            var ms = ModelState;
            if (!ms.IsValid)
                throw new Exception("Should not be possible");

            return Ok();
        }
    }

我的控制器不再被击中,而是从 Postman 那里得到了一个整洁的响应:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|f5f5b1a2-4dd202e6998784c3.",
    "errors": {
        "Name": [
            "'Name' is required."
        ],
        "Content": [
            "'Content' is required."
        ]
    }
}