我需要激活 .NET Core MVC 模型验证还是默认激活

Do I need to active .NET Core MVC Model Validation or is it activated by default

正在测试我的网站 API(nuget 包 Microsoft.AspNetCoreAll 2.0.5)我 运行 遇到了使用注释进行模型验证的奇怪问题。

我有(例如)这个控制器:

[HttpPost]
public IActionResult Create([FromBody] RequestModel request) 
{
  if (!ModelState.IsValid) 
  {
    return BadRequest(ModelState);
  }

  // create
  request.Name.DoSomething();      


  return Created(...);

}

我将我的 RequestModel 定义如下:

public class RequestModel
{
  [Required]
  public string Name {get; set};

}

我的问题虽然我将 RequestModel.Name 定义为 [Required] 它是空的(如果正文中的 json 中不存在名称。我认为不应该发生是因为它被标记为 [Required] 并自动显示为 ModelState 错误。

给定 this link to the specs 他们使用 Bind(....)。

所以我的问题是? 我是否必须每次都启用它,或者它应该开箱即用,或者它打算如何使用?

如果我用 [Required] 注释它,我会假设至少 ModelState.IsValid returns false 如果它不存在。

在我有多个对象相互嵌套的情况下,在 link 中使用 Bind 对我来说似乎有点复杂。


编辑 1:创建了一个 MVC 数据验证测试平台 为了更好地形象化我的意思,以便每个人都可以轻松地自己进行实验,我在 GitHub.

上创建了一个小演示 .NET Core MVC data validation test bed

您可以下载代码,使用 VS 2017 启动它,然后使用 swagger 自己尝试一下 ui。

有这个模型:

public class StringTestModel2
{
    [Required]
    public string RequiredStringValue { get; set; }
}

并使用该控制器进行测试:

  [HttpPost("stringValidationTest2")]
  [SwaggerOperation("StringValidationTest2")]
  public IActionResult StringValidationTest2([FromBody] StringTestModel2 request)
  {
    LogRequestModel("StringValidationTest2", request);

    if (!ModelState.IsValid)
    {
      LogBadRequest(ModelState);
      return BadRequest(ModelState);
    }

    LogPassedModelValidation("StringValidationTest2");

    return Ok(request);
  }

结果与预期相去甚远:

  1. 允许给出空值(不是字符串 "null")并且 return 200 OK
  2. 允许给一个 int returns 200 OK(它被转换为字符串)
  3. 允许提供双精度数 returns 200 OK(如果可能,它被转换为字符串,如果不能转换(混合点和分号 return 400 错误请求)
  4. 如果您只是发送空的大括号并保留 RequiredStringValue 未定义,它会通过并且 returns 200 OK(字符串为空)。

让我(暂时)得出以下结论之一:

  1. 要么 MVC 数据验证无法开箱即用
  2. 两者都没有按预期工作(如果将 属性 标记为 required,则应确保它存在)
  3. MVC 数据验证被破坏
  4. 要么 MVC 数据验证完全没用
  5. 我们遗漏了一些重要的点(比如 Bind[])

作为 using/deriving 的一部分,您可以从控制器自动获取 ModelValidation(我相信它在 MVC 中间件中),但不幸的是,这不包括空值检查。所以你需要显式检查参数是否为 NULL 以及 ModelState 检查。

[HttpPost]
public IActionResult Create([FromBody] RequestModel request) 
{
     if (request == null || !ModelState.IsValid) 
     {
         return BadRequest(ModelState);
     }

     ...

经过进一步试验,我找到了答案。

Does the data validation need to be activated?

答:这取决于你配置服务的方式:

不需要,使用的话不需要激活

services.AddMvc();

是的,使用需要激活

services.AddMvcCore()
  .AddDataAnnotations(); //this line activates it

这个 article 让我找到了答案。

我假设你使用

services.AddMvc();

所以它应该默认工作。

但它并不像您预期​​的那样工作:它不是 returning 400 状态代码,而是使模型状态无效并让您管理操作结果。 您可以创建属性 class 以自动 return "Bad request"

internal class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                new ApiError(
                    string.Join(" ",
                        context.ModelState.Values
                            .SelectMany(e => e.Errors)
                            .Select(e => e.ErrorMessage))));
        }
    }
}

其中 ApiError 是错误结果的自定义 ViewModel。

现在您可以使用此属性标记控制器或操作,以实现默认情况下您期望的行为。

如果您希望所有方法都有这种行为,只需将 AddMvc 行更改为如下内容:

services.AddMvc(config => config.Filters.Add(new ValidateModelAttribute()));