Post 使用模型 class 与 DTO 的操作

Post Action with model class vs DTO

此 MSDN link 解释了为什么在 Web API 中使用 DTO classes 是一个很好的做法。这是可以理解的,让我感到困惑的是在同一页中,post 方法使用模型 class 而不是简单的 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);
}

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

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

对于我的个人项目和工作中的项目,我制定的约定是我们将使用附加了 RequestResponse 的 DTO 对象来帮助维护和清洁 API 表面上大家都能看懂。

public CreateBookResponse CreateBook(CreateBookRequest request)
{
}

此外,在某些情况下,我们根据 Web 服务的需要使用 HttpResponseMessageIActionResult 或其他 return 值,但具有请求和响应对象明确定义使我们所有的开发人员,即使是像 ASP 这样的不同技能的开发人员,也很容易理解由此产生的 JSON 结构。

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

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

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

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

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

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

简答:由您决定。

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