使用 ModelState 验证 DTO

ban*_*neh 1 c# .net-core asp.net-core-webapi

我正在使用代码优先方法与实体框架一起创建 .NET Core WEB API 项目。我无法验证来自请求的输入,因为 ModelState 验证始终为真。

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

  • 数据访问层
  • 业务逻辑层
  • .NET 核心 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; }
}
Run Code Online (Sandbox Code Playgroud)

对应的 DTO:

public class GroupDto
{
    public long GroupId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

控制器方法:

[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);
}
Run Code Online (Sandbox Code Playgroud)

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

应如何验证 DTO?我想避免在两个地方重复相同的验证。是否应该创建额外的自定义 DtoValidator 或者我是否缺少一些东西,并且有办法执行这些验证。

Bry*_*wis 8

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

如果您使用的是 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

  • 我完全不知道 [ApiController] 的功能,谢谢!根据您提到的内容,我了解到我应该向 DTO 类添加相应的数据注释,这将是验证的第一步。但是 DataModel 的验证应该如何以及在哪里进行呢?当 BLL/服务收到此类对象时,可能会发生这种情况,我的理解正确吗?如果是这种方式,我应该为这些对象创建验证方法,还是有任何类似于 ModelState.IsValid 构造的东西我可以在这些对象上执行? (2认同)