使用ModelState

时间:2019-03-29 21:19:59

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

我正在使用Code First方法与实体框架一起创建.NET Core WEB API项目。我在验证来自请求的输入时遇到了麻烦,因为 ModelState验证始终为真。

我的应用程序由3层组成。

  • 数据访问层
  • 业务逻辑层
  • .NET Core API

DAL中的示例数据模型:

public class Group
{
    [Key]
    [Required]
    public long GroupId { get; set; }
    [Required]
    public string Name { get; set; }
    [Required(AllowEmptyStrings = false)]
    public string Description { get; set; }
    public DateTime CreationDate { get; set; }
    public bool IsActive { get; set; }
}

对应的DTO:

public class GroupDto
{
    public long GroupId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

控制器方法:

[HttpPost]
public IActionResult Post([FromBody] GroupDto groupDto)
{
    Group group = _mapper.Map<Group>(groupDto);
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    _groupService.Add(group);

    groupDto = _mapper.Map<GroupDto>(group);

     return Ok(groupDto);
}

据我所知,在当前状态下ModelState.IsValid将始终返回true,因为GroupDto没有通过DataAnnotations完成任何验证。

应如何验证DTO?我想避免在两个地方重复相同的验证。如果应该创建其他自定义DtoValidator,或者我错过了somtething,可以执行这些验证。

2 个答案:

答案 0 :(得分:3)

模型状态验证将在传入的模型(在您的情况下为GroupDto)中进行。仅仅因为您最终映射到Group类与验证的工作方式无关。您将需要在DTO中重复验证属性。这确实重复了代码,但是还允许您自定义规则,因为您可能希望也可能不需要DTO中的完全相同的设置。例如,您的主键。对于创建(POST),您不一定希望GroupId成为传递到控制器的必填字段,因为DB可能会自动生成该字段(取决于您的设置)。

如果使用的是ASP.Net Core 2.1或更高版本,则现在还可以将[ApiController]属性应用于控制器类,它将自动应用模型验证规则。这样就无需手动检查ModelState.IsValid。如果模型无效,系统将自动返回400 Bad Request。 (https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2#annotation-with-apicontroller-attribute

答案 1 :(得分:0)

DTO 和模型是不同的东西,所以应该在两者中都设置 DataAnnotation 属性。 “ModelState.IsValid”检查的主题是您处理的函数的输入。因此,在您的代码中,被检查的模型是 GroupDto。

$timeout = 5; // seconds
$fp = fsockopen($server, $port, $errno, $errstr, 5); // The timeout set here doesn't affect the streaming timeout
if (!$fp) {
    echo "$errstr ($errno)<br>\n";
} else {
    $urlstring = "GET ".$url." HTTP/1.0\r\n\r\n";
    fputs ($fp, $urlstring);
    while ($str = trim(fgets($fp, 4096))){
        header($str);                   
    }
    $start = time();
    $now = time();
    while ( $now - $start < $timeout ) {
        echo fread( $fp, 4096 );
        $now = time();
    }
    fclose($fp);
}

无论如何,为了使控制器中的“模型验证”干净利落,我建议使用自定义的 ValidateModelAttribute,就像这样。

[HttpPost]
public IActionResult Post([FromBody] GroupDto groupDto)
{
    Group group = _mapper.Map<Group>(groupDto);
    if (!ModelState.IsValid) // this line will check the validation inside GroupDto

然后你可以像这样在控制器上编写验证。

using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace MyWeb.App.Filter
{
  public class ValidateModelAttribute : ActionFilterAttribute
  {
    public override void OnActionExecuting( HttpActionContext actionContext )
    {
      if( !actionContext.ModelState.IsValid )
      {
        actionContext.Response = actionContext.Request.CreateErrorResponse(
          HttpStatusCode.BadRequest, actionContext.ModelState ); 
        // Path to the error in request content will be sent to the client, so helpful!
      }
    }
  }
}

希望这能回答问题。