您是否应该为参数创建一个 DTO,为正文创建一个 DTO,为查询创建一个,...?

Should you create one DTO for the params, one for the body, one for the queries, ...?

所以我们假设您有一个 API 端点需要

您想创建一个 DTO 来获取和验证所有这些值。正如您可能知道的那样,不可能有一个 DTO class 来处理整个请求,所以这是行不通的:

public class Dto
{
    [FromRoute]
    public int Id { get; set; }

    [FromBody]
    public int Foo { get; set; }
}

解析器只会设置Foo字段而忽略Id字段。当调用 https://localhost:5001/foo/1 时,Id 字段将只有默认值 0,而不是 1。可以为 url 参数创建一个 DTO,一个用于正文,一个用于查询像这样

public class ParamsDto
{
    public int FirstId { get; set; }
    public int SecondId { get; set; }
}

// ... same for the body and queries ...

然后你可以像这样标记那些 dtos [FromRoute] ParamsDto paramsDto, [FromBody] BodyDto bodyDto, [FromQuery] QueryDto queryDto。您可能有一个多层架构,并且您的 Web API 项目与一个需要 DTO 的层进行通信,该 DTO 是上面显示的所有 3 个 DTO 的组合。所以你必须将这 3 个对象合并到一个大的 DTO,例如使用 AutoMapper..

将请求 DTO 转换为命令对象(来自 Mediatr

在文件夹结构、命名约定等方面是否有任何处理多个 DTO 的最佳实践......?因为你可能必须为参数 UpdateProductAmountFromOrderByIdsParamsDto 和正文 UpdateProductAmountFromOrderByIdsBodyDto。我知道这不是最好的例子,因为这里并不真正需要 DTO,但我们假设您将对 ID 进行复杂的验证,并且正文包含多个值。您不希望在控制器文件中有那么多验证注释。就将这些单独的 DTO 合并为一个大的而言,这种方法对我来说真的很混乱,所以也许有更好的方法。

这是 asp.net 核心 3.0 的工作演示:

型号:

public class Dto
{
    [FromRoute(Name = "")]
    public Test Test { get; set; }

    [FromBody]
    public Sample Sample { get; set; }
    public ApiVersion ApiVersion { get; set; }
}
public class Test
{
    public string Col1 { get; set; }
    public int Col2 { get; set; }
    public bool Col3 { get; set; }
}
public class ApiVersion
{
    public int VersionId { get; set; }
    public string VersionName { get; set; }
}
public class Sample
{
    public int Foo { get; set; }
    public string Name { get; set; }
}

控制器:

[ApiController]
[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET: api/<controller>
    [HttpGet("col1/{col1}/col2/{col2}/col3/{col3}")]
    public async Task<IActionResult> Get([FromQuery]Dto dto)
    {           
        return Ok(dto);
    }
}

请求url:https://localhost:44393/api/values/col1/1/col2/2/col3/true?apiversion.versionId=1&apiversion.versionName=aaa

结果:

更新:

public class Dto
{
    //[FromRoute(Name = "")]
    //public Test Test { get; set; }
    [FromRoute]
    public string Col1 { get; set; }
    [FromRoute]
    public int Col2 { get; set; }
    [FromRoute]
    public bool Col3 { get; set; }
    [FromBody]
    public Sample Sample { get; set; }
    public ApiVersion ApiVersion { get; set; }
}