REST API - DTO或不是?

ben*_*bjo 133 java rest spring dto hateoas

我目前正在为一个项目创建一个REST-API,并且正在阅读关于最佳实践的文章.许多人似乎反对DTO,只是暴露域模型,而其他人似乎认为DTO(或用户模型或任何你想称之为的)是不好的做法.就个人而言,我认为这篇文章很有意义.

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

我们的API主要是为了让其他客户端可以使用数据而创建的,但是如果我们做得对,我们也希望尽可能将它用于我们自己的Web GUI.

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

我该如何解决这个问题?我正在考虑在我们的域模型上使用Jackson mixins来控制在给定不同场景的情况下会暴露哪些数据.或者我们应该一直使用DTO - 即使考虑到它的缺点和争议?

cas*_*lin 211

为什么要在REST API中使用DTO

DTO代表D ata T ransfer O bject.

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

REST资源表示不需要与持久性模型具有相同的属性:您可能需要省略,添加或重命名属性.

只是提到暴露DTO而不是持久性模型的一些好处:

  • DTO可以根据您的需求进行定制,并且在仅暴露持久性实体的一组属性时它们非常棒.您不需要注释,例如@XmlTransient@JsonIgnore避免某些属性的序列化.
  • 通过使用DTO,您将避免持久性实体中的注释,也就是说,持久性实体不会因​​非持久性相关注释而膨胀;
  • 您可以完全控制在创建或更新资源时接收的属性;
  • 如果您使用的是Swagger,则可以使用@ApiModel@ApiModelProperty注释来记录您的API模型,而不会弄乱您的持久性实体;
  • 您可以为每个API版本使用不同的DTO;
  • 映射关系时,您将拥有更大的灵活性;
  • 您可以为不同的媒体类型设置不同的DTO;
  • 您的DTO可以包含HATEOAS的链接列表.这是不应该添加到持久性对象的那种东西.

处理样板代码

你不会需要你的持久实体反之亦然映射到DTO的副码的手动.有很多映射框架,你可以用它来做到这一点.例如,看看MapStruct,它是基于注释的,并作为Maven注释处理器.它适用于CDI和基于Spring的应用程序.

还要考虑使用Project Lombok来阻止您编写getter,setter,equals,hashcode和toString方法.


相关:要为您的DTO课程提供更好的名称,请参阅此答案.

  • 在REST API中使用DTO而不是域实体也存在架构原因.不应更改REST API以避免破坏现有客户端.如果直接在API中使用域模型,则会在API和域模型之间创建不需要的耦合.根据服务松散耦合设计原则,服务合同不应与服务逻辑或实现细节紧密耦合. (7认同)
  • @benbjo这取决于你.我通常只将最复杂的实体映射到DTO,我不希望暴露所有属性的实体和具有大量关系的实体.DTO让我可以灵活地在HATEOAS中使用*链接*列表.这是我不会添加到持久性对象的那种东西. (6认同)
  • 亲爱的downvoter,你能否至少解释你的downvote的原因? (6认同)
  • 如果我确实采用DTO方式,你会将_all_域对象映射到DTO还是只是那些不相同的对象?另外,您如何解决基于不同场景/上下文暴露数据的问题?每个域对象有多个DTO? (2认同)
  • @molin非常感谢您提供的信息和建议.我一定会查看MapStruct.一目了然它看起来非常适合我的需求. (2认同)
  • 我查看了MapStruct用户指南,它似乎是一个非常先进的工具,适用于这种情况,并涵盖了许多边缘情况. (2认同)
  • 真正的问题是我们是否应该为不同的动词使用不同的 DTO (2认同)

Dav*_*iro 21

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

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


Arg*_*b32 11

我倾向于使用DTO.

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

域对象的展示可能会导致安全问题和数据泄漏.杰克逊的注释似乎可以解决这个问题,但是很容易犯错误并暴露不应该暴露的数据.在设计DTO课程时,犯这样的错误要困难得多.

另一方面,DTO方法的缺点可以通过对象映射和Lombok等较少的样板来减少.


Mar*_*rey 6

正如您已经说过的那样,这显然是一个与意见相关的问题.我自己更倾向于采用No-DTOs方法,因为您需要所有的样板代码.

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

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

  • 我同意我的问题与意见有关(并且气馁),但我也在寻找有关如何解决问题的提示.我会对你的杰克逊插件采取很多措施,但是你认为使用mixins来控制在不同的场景中应该暴露哪些数据是一件好事吗? (2认同)