我需要激活 .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);
}
结果与预期相去甚远:
- 允许给出空值(不是字符串 "null")并且 return 200 OK
- 允许给一个 int returns 200 OK(它被转换为字符串)
- 允许提供双精度数 returns 200 OK(如果可能,它被转换为字符串,如果不能转换(混合点和分号 return 400 错误请求)
- 如果您只是发送空的大括号并保留 RequiredStringValue 未定义,它会通过并且 returns 200 OK(字符串为空)。
让我(暂时)得出以下结论之一:
- 要么 MVC 数据验证无法开箱即用
- 两者都没有按预期工作(如果将 属性 标记为 required,则应确保它存在)
- MVC 数据验证被破坏
- 要么 MVC 数据验证完全没用
- 我们遗漏了一些重要的点(比如 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()));
正在测试我的网站 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);
}
结果与预期相去甚远:
- 允许给出空值(不是字符串 "null")并且 return 200 OK
- 允许给一个 int returns 200 OK(它被转换为字符串)
- 允许提供双精度数 returns 200 OK(如果可能,它被转换为字符串,如果不能转换(混合点和分号 return 400 错误请求)
- 如果您只是发送空的大括号并保留 RequiredStringValue 未定义,它会通过并且 returns 200 OK(字符串为空)。
让我(暂时)得出以下结论之一:
- 要么 MVC 数据验证无法开箱即用
- 两者都没有按预期工作(如果将 属性 标记为 required,则应确保它存在)
- MVC 数据验证被破坏
- 要么 MVC 数据验证完全没用
- 我们遗漏了一些重要的点(比如 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()));