REST API – DTO或不?
我目前正在为一个项目创build一个REST-API,并且正在阅读关于最佳实践的文章。 许多人似乎反对DTOs,只是公开领域模型,而其他人似乎认为DTO(或用户模型或任何你想称之为)是不好的做法。 我个人认为这篇文章很有意义。
但是,我也了解DTO的所有额外映射代码的缺陷,可能与它们的DTO对等体完全相同的域模型等等。
我们的API大部分都是为了让其他客户端可以使用数据而创build的,但是如果我们做得正确的话,我们也可以在我们自己的Web GUI上使用它。
问题是我们可能不想将所有域数据公开给其他客户端用户。 大部分数据只会在我们自己的Web应用程序中有意义。 另外,我们可能不希望在所有场景中公开有关对象的所有数据,特别是与其他对象的关系等等。 例如,如果我们公开一个特定对象的列表,我们不一定要公开整个对象的层次; 使对象的孩子不会暴露,但可以通过链接发现(仇恨)。
我应该如何解决这个问题呢? 我正考虑在我们的领域模型上使用Jackson mixins来控制在不同场景下会暴露的数据。 还是应该一直使用DTO – 即使有缺点和争议?
为什么你应该在你的REST API中使用DTO
DTO代表D ata T ransfer O bject 。
这个模式的创build目的非常明确:就像Web服务一样, 将数据传输到远程接口 。 这种模式非常适合于REST API,DTOs在长期运行中会给你更多的灵活性 。 而您的REST资源表示不需要具有与持久对象相同的属性。
只要提到几个好处:
- DTO可以根据您的需要进行定制 ,当暴露持久性实体的一组属性时,它们非常棒。 您将不需要
@XmlTransient
和@JsonIgnore
等注释来避免某些属性的序列化。 - 通过使用DTO,您可以避免在持久性实体中留下批注 ,也就是说,您的持久性实体不会被与非持久性相关的注释臃肿;
- 在创build或更新资源时,您将完全控制您正在接收的属性;
- 如果您使用Swagger ,则可以使用
@ApiModel
和@ApiModelProperty
注释来logging您的API模型,而不会弄乱您的持久性实体; - 您可以为每个版本的API使用不同的DTO;
- 映射关系时你会有更多的灵活性;
- 不同的媒体types可以有不同的DTO;
- 你的DTO可以有一个HATEOAS的链接列表。 这是不应该添加到持久对象的东西。
映射框架
您不需要将持久性实体映射到DTO,反之亦然。 有许多映射框架可以用来做到这一点。 例如,看看MapStruct ,它是基于注释的,并作为Maven Annotation Processor工作。 它也适用于CDI和基于Spring的应用程序。
相关:给你的DTO类更好的名字,参考这个答案 。
我倾向于使用DTO。
我不喜欢这些缺点,但似乎其他的select更糟糕:
域对象的说明可能会导致安全问题和数据泄漏。 jackson的注释似乎可以解决这个问题,但是犯了一个错误和泄露不应该暴露的数据太容易了。 在deviseDTO课程时,犯这样的错误就更困难了。
另一方面,DTO方法的缺点可以通过诸如对象映射和Lombok之类的东西来减less。
当你的API是公开的,你必须支持多个版本,你必须去与DTOs。
另一方面,如果它是私有API,并且您同时控制客户端和服务器,则我倾向于跳过DTO并直接暴露域模型。
正如你已经说过,这显然是一个意见相关的问题。 我自己更喜欢No-DTO的方法,仅仅是因为你需要的所有样板代码。
这主要是对json / rest api的响应方面。 我甚至写了一个jackson插件,以避免写这些情况下的许多JSON视图/filter: https : //github.com/Antibrumm/jackson-antpathfilter
另一方面,DTO在这些API的请求input端是一件好事。 例如,直接在实体上工作可能很难考虑双向关系。 你也不想让调用者修改一个“creator”属性。 所以你需要在映射这些请求的时候不允许某些字段。