我是否需要激活.NET Core MVC模型验证,或者是否默认激活

时间:2018-03-19 15:31:34

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

测试我的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(如果身体的json中不存在Name。我认​​为不应该发生,因为它被标记为[Required]并自动显示为ModelState错误。

鉴于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. 允许返回一个返回200 OK(它转换为字符串)
  3. 允许使用double并返回200 OK(如果可能,它会转换为字符串,如果不能转换(混合点和分号返回400 Bad Request)
  4. 如果你只是发送空的大括号并且保留RequiredStringValue undefined它会传递并返回200 OK(字符串为null)。
  5. 离开我(现在),得出以下结论之一:

    1. MVC数据验证无法开箱即用
    2. 要么不按预期工作(如果根据需要标记属性,应该确保它在那里)
    3. MVC数据验证被破坏
    4. 要么MVC数据验证完全无用
    5. 我们缺少一些重要的观点(如Bind [])

3 个答案:

答案 0 :(得分:2)

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

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

     ...

答案 1 :(得分:1)

我假设您使用

services.AddMvc();

所以它应该默认工作。

但它并不像你期望的那样工作:它不是返回400状态代码,而是使模型状态无效并让你管理动作结果。 您可以创建一个属性类来自动返回"错误请求"

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 :(得分:0)

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

  

是否需要激活数据验证?

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

不,如果您使用

,则无需激活
services.AddMvc();

是的,如果您使用

,则需要激活它
services.AddMvcCore()
  .AddDataAnnotations(); //this line activates it

article让我得到答案。