REST API-是否有DTO?[关闭]


154

我目前正在为一个项目创建REST-API,并且一直在阅读有关最佳实践的文章。许多人似乎反对DTO并只是公开域模型,而其他人似乎认为DTO(或用户模型或任何您想称呼的东西)是不好的做法。我个人认为这篇文章很有道理。

但是,我还了解了所有其他映射代码,域模型可能与其DTO对应对象100%相同的DTO的缺点。

我们的API主要是为了使其他客户端可以使用数据而创建的,但是,如果操作正确,我们还将尽可能地将其用于我们自己的Web GUI。

问题是我们可能不想将所有域数据公开给其他客户端用户。许多数据仅在我们自己的Web应用程序中才有意义。另外,我们可能不希望在所有情况下都公开有关某个对象的所有数据,尤其是与其他对象的关系等等。例如,如果我们公开一个特定对象的列表,则我们不一定要公开整个对象层次结构。这样就不会暴露对象的子对象,而是可以通过链接(阴影)来发现它。

我应该如何解决这个问题?我正在考虑在我们的域模型上使用Jackson混合来控制在不同情况下将公开哪些数据。还是考虑到DTO的缺点和争议,我们是否应该一路使用DTO?


9
如果这个问题解决了,不要感到惊讶。更多的是基于讨论的问题,这意味着没有明确的正确答案。询问不同的人,您将得到不同的答案。
本·瑟利


7
@pinkpanther这是一篇很棒的文章,很可惜它不再可用。这是Web存档的缓存版本
cassiomolin

Answers:


251

为什么要在REST API中使用DTO

DTO代表d ATA 牛逼转让(BOT)Ø bject

创建此模式的目的非常明确:将数据传输到远程接口,就像Web服务一样。这种模式非常适合REST API ,从长远来看,DTO将为您提供更大的灵活性

该代表的车型领域的应用代表了和模型通过您的API处理的数据是(或者至少应该是)不同的关注点,应该去耦彼此。您不希望在添加,删除或重命名应用程序域模型中的字段时破坏API客户端。

服务层在域/持久性模型上运行时,API控制器应在一组不同的模型上运行。例如,随着域/持久性模型的发展以支持新的业务需求,您可能希望创建API模型的新版本以支持这些更改。随着新版本的发布,您可能还希望弃用旧版本的API。当事物分离时,这是完全有可能实现的。


仅提及公开DTO而不是持久性模型的一些好处:

  • 持久性模型与API模型分离。

  • DTO可以根据您的需求进行定制,当仅公开持久性实体的一组属性时,它们非常有用。您将不需要诸如@XmlTransient和的注释,@JsonIgnore以避免某些属性的序列化。

  • 通过使用DTO,您将避免在持久性实体中出现批注,也就是说,持久性实体不会因​​与持久性无关的批注而肿。

  • 您将完全控制在创建或更新资源时所接收的属性。

  • 如果使用的是Swagger,则可以使用@ApiModel@ApiModelProperty注释来记录您的API模型,而不会弄乱您的持久性实体。

  • 您可以为每个API版本使用不同的DTO。

  • 映射关系时,您将具有更大的灵活性。

  • 您可以为不同的媒体类型使用不同的DTO。

  • 您的DTO可以具有HATEOAS的链接列表。这种事情不应该添加到持久性对象中。使用Spring HATEOAS时,您可以扩展DTO类RepresentationModel(以前称为ResourceSupport)或将其包装为EntityModel(以前称为Resource<T>)。

处理样板代码

您无需手动将持久性实体映射到DTO,反之亦然。有很多映射框架,你可以用它来做到这一点。例如,看看基于注释的MapStruct,它可以作为Maven注释处理器使用。它在CDI和基于Spring的应用程序中都能很好地工作。

您可能还需要考虑龙目岛生成getter,setter方法,equals()hashcode()toString()为你的方法。


相关:为您的DTO类提供更好的名称,请参考此答案


2
如果我确实采用了DTO的方式,那么您将所有域对象都映射到DTO还是只是将它们映射到不同的对象?此外,您将如何解决基于不同方案/上下文公开数据的问题?每个域对象有多个DTO?
benbjo

6
@benbjo由您决定。我通常只将最复杂的实体映射到DTO,即我不想公开所有属性的实体以及具有很多关系的实体。DTO使我可以灵活地拥有要在HATEOAS中使用的链接列表。那就是我不会添加到持久性对象中的那种东西。
cassiomolin '16

2
@molin非常感谢您提供的信息和建议。我一定会检查MapStruct的。乍一看,它非常适合我的需求。
benbjo

6
亲爱的下选民,您至少可以解释一下下选的原因吗?
cassiomolin '16

8
还有一个架构上的原因,即在REST API中使用DTO代替域实体。REST API不应更改,以免破坏现有客户端。如果直接在API中使用域模型,则会在API和域模型之间创建不良耦合。根据服务松散耦合设计原则,服务合同不应与服务逻辑或实现细节紧密耦合。
Paulo Merson

25

当您的API是公开的并且必须支持多个版本时,必须使用DTO。

另一方面,如果它是私有API,并且您同时控制客户端和服务器,则我倾向于跳过DTO并直​​接公开域模型。


我在最后一部分同意您的观点,而且通常会这样做,但这是我的第一个公共API。我将考虑您对公共部分使用DTO的看法。即使“吃自己的狗粮”是一个很好的原则,API的私有部分和公共部分的确应该分开。
Benbjo

11

我倾向于使用DTO。

我不喜欢缺点,但是其他选择似乎更糟:

域对象的公开可能导致安全问题和数据泄漏。杰克逊(Jackson)批注似乎可以解决问题,但是很容易犯一个错误并公开不应公开的数据。在设计DTO类时,犯这样的错误要困难得多。

另一方面,DTO方法的弊端可以通过对象到对象的映射和Lombok减少样板来减少。


9

正如您已经说过的那样,这显然是与意见相关的问题。我本人更喜欢No-DTO方法,这仅仅是因为您需要所有样板代码。

这主要适用于json / rest api的响应端。在这些情况下,我什至写了一个jackson插件以避免编写许多json视图/过滤器:https : //github.com/Antibrumm/jackson-antpathfilter

另一方面,在此类API的请求输入端,DTO是一件好事。直接考虑实体的工作可能非常困难,例如考虑到双向关系。另外,您实际上并不想让调用者修改“创建者”属性。因此,您将需要在此类请求的映射期间禁止某些字段。


2
我同意我的问题在某种程度上与意见相关(并且不建议这样做),但是我也在寻找有关如何解决问题的技巧。我将在您的Jackson插件中做很多工作,但是您认为使用mixins控制应该在不同方案中公开哪些数据的做法很好吗?
benbjo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.