我目前正在为项目创建REST-API,并且正在阅读有关最佳实践的文章。许多人似乎反对DTO,只是暴露域模型,而其他人似乎认为DTO(或用户模型或任何你想称之为的)是不好的做法。就个人而言,我认为this article很有意义。
但是,我也理解DTO的缺点,包括所有额外的映射代码,可能与其DTO对应物100%相同的域模型等等。
我们的API主要是为了让其他客户端可以使用数据而创建的,但是如果我们做得对,我们也希望在可能的情况下将它用于我们自己的Web GUI。
问题是我们可能不希望将所有域数据公开给其他客户端用户。大部分数据只在我们自己的Web应用程序中才有意义。此外,我们可能不希望在所有方案中公开有关对象的所有数据,尤其是与其他对象的关系等。例如,如果我们公开特定对象的列表,我们不一定要暴露整个对象层次结构;这样对象的孩子就不会暴露,但可以通过链接(hateoas)发现。
我应该如何解决这个问题?我正在考虑在我们的域模型上使用Jackson mixins来控制在给定不同场景的情况下会暴露哪些数据。或者我们应该一直使用DTO - 即使考虑到它的缺点和争议?
答案 0 :(得分:185)
DTO 代表Data Transfer Object。
此模式的创建目的非常明确:将数据传输到远程接口 ,就像 Web服务一样。这种模式非常适合REST API,从长远来看,DTO将为您提供更多的灵活性。
REST资源表示不需要与持久性模型具有相同的属性:您可能需要省略,添加或重命名属性。
只是提到暴露DTO而不是持久性模型的一些好处:
从API模型中解除持久性模型。
DTO可以根据您的需求进行定制,只展示持久性实体的一组属性时它们非常棒。您不需要@XmlTransient
和@JsonIgnore
等注释来避免某些属性的序列化。
通过使用DTO,您将避免持久性实体中的地狱注释,也就是说,您的持久性实体不会因非持久性相关注释而膨胀。
在创建或更新资源时,您将对所接收的属性进行完全控制。
如果您使用的是Swagger,则可以使用@ApiModel
和@ApiModelProperty
注释来记录您的API模型,而不会弄乱您的持久性实体。
您可以为每个版本的API使用不同的DTO。
在映射关系时,您将拥有更大的灵活性。
您可以针对不同的媒体类型使用不同的DTO。
您的DTO可以包含HATEOAS的链接列表。这是不应该添加到持久性对象的那种东西。
您不需要将持久性实体映射到DTO,反之亦然手动。您可以使用many mapping frameworks来执行此操作。例如,看看MapStruct,它是基于注释的,并作为Maven注释处理器。它适用于CDI和基于Spring的应用程序。
另请考虑Lombok为您生成getter,setter,equals()
,hashcode()
和toString()
方法。
相关:要为您的DTO课程提供更好的名称,请参阅此answer。
答案 1 :(得分:18)
如果您的API是公开的,并且您必须支持多个版本,则必须使用DTO。
另一方面,如果它是私有API并且您同时控制客户端和服务器,我倾向于跳过DTO并直接暴露域模型。
答案 2 :(得分:11)
我倾向于使用DTO。
我不喜欢这些缺点,但似乎其他选择更糟糕:
域对象的展示可能会导致安全问题和数据泄露。 杰克逊的注释似乎可以解决这个问题,但是很容易犯错误并暴露不应该暴露的数据。 在设计DTO课程时,犯这样的错误要困难得多。
另一方面,DTO方法的缺点可以通过对象到对象映射和Lombok之类的东西来减少,以减少样板。
答案 3 :(得分:5)
正如您已经说过的那样,这显然是一个与意见相关的问题。我自己更喜欢No-DTOs方法,仅仅因为你需要所有的样板代码。
这主要适用于json / rest api的响应方。我甚至写了一个jackson插件,以避免为这些情况编写许多json视图/过滤器:https://github.com/Antibrumm/jackson-antpathfilter
另一方面,DTO在这些API的请求输入端是一件好事。例如,考虑到双向关系,直接在实体上工作可能非常困难。另外,你真的不想让调用者修改一个"创建者"属性例如。因此,您需要在映射此类请求期间禁止某些字段。