【问题标题】:Post Action with model class vs DTO模型类与 DTO 的 Post Action
【发布时间】:2019-02-01 13:21:21
【问题描述】:

此 MSDN link 解释了为什么将 DTO 类用于 Web API 是一种很好的做法。这是可以理解的,让我困惑的是在同一个页面中,post 方法使用模型类而不是简单的 DTO 类,如下所示:

[ResponseType(typeof(BookDTO))]
public async Task<IHttpActionResult> PostBook(Book book)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    db.Books.Add(book);
    await db.SaveChangesAsync();

    // New code:
    // Load author name
    db.Entry(book).Reference(x => x.Author).Load();

    var dto = new BookDTO()
    {
        Id = book.Id,
        Title = book.Title,
        AuthorName = book.Author.Name
    };

    return CreatedAtRoute("DefaultApi", new { id = book.Id }, dto);
}

我想我的问题是:Post/Put 操作应该采用模型还是 DTO 参数?

更新: 从答案看来,即使在发布/放置操作的情况下,也建议使用 dto,但这会导致另一个问题,如何在发布操作的情况下从 dto 映射到模型类?假设我们使用 AutoMapper,我发现许多链接,例如 thisthis 警告不要在反向映射中使用它(即 dto => Model 类)。

【问题讨论】:

  • 方法参数应该采用DTO。然后将 DTO 翻译成对应的模型。
  • 我同意这是一个不好的例子,您应该始终使用 DTO。创建新实体(如示例所示)可能是唯一的例外,但我不习惯也不建议这样做。

标签: c# asp.net-web-api asp.net-core .net-core dto


【解决方案1】:

首先,是的,您应该始终使用 DTO。无论您是在处理常规网站还是 API,您都应该永远直接保存从发布数据实例化的对象。这打开了一个巨大的安全漏洞,人们可以在其中操纵帖子数据并进行各种恶作剧。具有讽刺意味的是,您实际上可以绑定到您的实体类,但如果这样做,您永远不应该保存该实例,而是创建一个新实例,映射来自已发布实例的数据,然后保存您创建的实例 - 重要的是部分永远不会保存发布的实例。使用视图模型/DTO 只会更明显地表明您应该执行等式的映射部分,因此这是推荐的方法。

就 AutoMapper 而言,建议不要使用反向映射,因为在映射到将要保存到数据库的内容时涉及许多细微​​差别。特别是当你有像实体框架这样的 ORM 时。您可以使用 AutoMapper,但您只需要了解所有这些细微差别并相应地处理它们。一般来说,在这些情况下,手动进行映射可能更容易,因为它通常涉及像 AutoMapper 这样的配置,从长远来看,您不会为自己节省太多精力。手动映射就像听起来一样。如果您正在创建一个新的Book,那么您只需新建一个Book 的实例。如果您正在修改现有的Book,您可以从数据库中提取该实例。无论哪种方式,您现在都有一个Book 的实例和一个类似BookDTO 的实例,它是从发布数据创建的。然后你就:

book.Title = bookDto.Tile;
// etc.

对于诸如作者关系之类的事情,您可能需要进行额外的查询。例如:

var author = _context.Authors.SingleOrDefault(x => x.Name == bookDto.AuthorName);
book.Author = author;

【讨论】:

    【解决方案2】:

    对于我的个人项目和工作中的项目,我制定的约定是我们将使用附加了 RequestResponse 的 DTO 对象,以帮助可维护性和干净的 API 表面,每个人都可以理解.

    public CreateBookResponse CreateBook(CreateBookRequest request)
    {
    }
    

    此外,在某些情况下,我们会根据 Web 服务的需要使用 HttpResponseMessageIActionResult 或其他返回值,但是明确定义请求和响应对象使我们所有的人都非常容易开发人员,即使是 ASP 等不同技能的开发人员,也能理解生成的 JSON 结构。

    我建议不要直接公开业务实体或数据访问对象。其中每一个在您的应用程序架构中都有不同的用途,您可能会暴露太多细节,或者发现维护横切关注点很困难。

    【讨论】:

    • 基本上,我们不使用任何类型的映射器。 DTO 是完全独立的,与我们架构中的其他对象没有交互或关系。 DTO 总是足够小并且只写一次,通常我们认为不需要它。我们不希望以任何方式将我们的 DTO 绑定到任何其他对象的复杂性,即使通过映射也是如此。 DTO 需要能够非常轻松地更改,架构的其余部分也是如此,在我看来,映射可能会引起关注。但是,它可能不适合你。
    【解决方案3】:

    简答:由你决定。

    更长的答案:我想将 DTO 视为层(例如服务)以简单/扁平的数据结构从外部接收数据或将数据暴露给外部的方式。根据这个定义,Web Api 中的模型只是一种 DTO,您可以在其中添加验证属性(例如,Required、MinLength 等),方便 api 控制器通过检查 ModelState.IsValid(它是错误)。但是您也可以在 DTO 类中添加这些属性。所以其实差别不大。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 1970-01-01
      • 2016-12-28
      • 2013-01-29
      • 1970-01-01
      • 2012-01-22
      • 2012-06-16
      相关资源
      最近更新 更多