REST API - dto还是不是?

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

但是,我也理解dto的缺点,包括所有额外的映射代码、可能与dto对应物100%相同的领域模型等等。

我们的API主要是为了让其他客户端可以使用数据而创建的,但是如果我们做得对,我们也想在可能的情况下将其用于我们自己的web GUI。

问题是,我们可能不想向其他客户端用户公开所有的域数据。大部分数据只在我们自己的web应用程序中才有意义。此外,我们可能不希望在所有场景中公开关于一个对象的所有数据,特别是与其他对象的关系等等。例如,如果我们公开一个特定对象的列表,我们不一定要公开整个对象层次结构;这样对象的子对象就不会被暴露,而是可以通过链接(hateoas)被发现。

我该如何着手解决这个问题呢?我在考虑在我们的领域模型上使用Jackson mixins来控制在不同的场景下会暴露哪些数据。或者我们应该一直使用dto——即使考虑到它的缺点和争议?

115623 次浏览

正如你已经说过的,这显然是一个与意见有关的问题。我本人更倾向于no - dto方法,仅仅是因为您需要的所有样板代码。

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

另一方面,dto在这类api的请求输入端是一件好事。例如,考虑到双向关系,直接处理实体是相当困难的。另外,你也不想让调用者修改一个“creator”属性。因此,在映射此类请求期间,您需要禁用某些字段。

如果API是公共的,并且必须支持多个版本,那么就必须使用dto。

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

我倾向于使用dto。

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

公开域对象可能导致安全问题和数据泄露。 Jackson注释似乎可以解决问题,但它很容易犯错误并暴露不应该暴露的数据。 在设计DTO类时,犯这样的错误要难得多

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

为什么应该在REST API中使用dto

DTO代表Data T transferer Object

此模式创建的目的非常明确:传输数据到远端接口,就像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类扩展< a href = " https://docs.spring.io/spring-hateoas/docs/current/api/org/springframework/hateoas/RepresentationModel.html " rel = " noreferrer " > RepresentationModel < / >(原名< a href = " https://docs.spring.io/spring-hateoas/docs/0.25.0.RELEASE/api/org/springframework/hateoas/ResourceSupport.html " rel = " noreferrer " > ResourceSupport < / >)或包装与< a href = " https://docs.spring.io/spring-hateoas/docs/current/api/org/springframework/hateoas/EntityModel.html "rel="noreferrer">EntityModel(以前称为Resource<T>)。

处理样板代码

你不需要将持久化实体映射到dto,反之亦然mannually。你可以使用许多映射框架来做这件事。例如,看一下MapStruct,它是基于注释的,作为Maven注释处理器工作。它在CDI和基于spring的应用程序中都工作得很好。

你也可以考虑使用Lombok来为你生成getter、setter、equals()hashcode()toString()方法。


相关:为了给你的DTO类起更好的名字,请参考这个答案