【问题标题】:Do I need to active .NET Core MVC Model Validation or is it activated by default我需要激活 .NET Core MVC 模型验证还是默认激活
【发布时间】:2018-08-28 05:38:05
【问题描述】:

测试我的 Web 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],但它为 null(如果 Name 不存在于正文中的 json 中。我认为这不应该发生,因为它被标记为 [Required] 并自动显示为模型状态错误。

鉴于this link to the specs,他们使用 Bind(....)。

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

如果我用 [Required] 对其进行注释,我会假设至少 ModelState.IsValid 如果不存在则返回 false。

在我有多个对象相互嵌套的情况下,在链接中使用 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(不是字符串“null”)并返回 200 OK
  2. 允许提供 int 并返回 200 OK(它被转换为字符串)
  3. 允许提供双精度并返回 200 OK(如果可能,它会转换为字符串,如果不可转换(混合点和分号返回 400 Bad Request)
  4. 如果您只是发送空的大括号并将RequiredStringValue 保留为未定义,则它通过并返回200 OK(字符串为空)。

让我(现在)得出以下结论之一:

  1. 两种 MVC 数据验证都不能开箱即用
  2. 其中任何一个都无法按预期工作(如果有人根据需要标记属性,则应确保它存在)
  3. 任何一个 MVC 数据验证被破坏
  4. 任何一种 MVC 数据验证都完全没用
  5. 我们遗漏了一些重要的点(例如 Bind[])

【问题讨论】:

  • 它应该开箱即用。你不需要激活任何东西。如果您不想在每个控制器操作中检查模型状态,您可以使用操作过滤器。
  • 如果它应该开箱即用,那么所需的名称如何为空并且验证不会自动抛出错误?
  • 您确定该字符串不包含空格还是可能发生任何类似的事情。
  • 实际上,如果它只包含空格,它甚至应该可以工作。你能用上面添加的模型重现这个问题吗?还是只发生在具有嵌套对象的模型上?
  • 嗯,空值不在任何嵌套类中,而是在“根”中。我目前正在构建一个专用的测试应用程序。希望明天完成。

标签: c# asp.net-core asp.net-core-mvc asp.net-core-2.0 asp.net-core-webapi


【解决方案1】:

我假设你使用

services.AddMvc();

所以它应该默认工作。

但它并没有像您期望的那样工作:它不是返回 400 状态代码,而是使模型状态无效并让您管理操作结果。 你可以创建一个属性类来自动返回“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()));

【讨论】:

    【解决方案2】:

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

    是否需要激活数据验证?

    答案:这取决于您的配置服务方法:

    不,如果你使用它不需要激活它

    services.AddMvc();
    

    是的,如果你使用它需要激活它

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

    这个article 让我找到了答案。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 在编辑 1 中,我证明它不能开箱即用(至少不像预期的那样)
      猜你喜欢
      • 2014-05-24
      • 2019-10-22
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      • 2017-08-10
      • 2016-06-27
      • 2013-11-17
      相关资源
      最近更新 更多