Java REST API - DTO 与否?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36174516/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:31:44  来源:igfitidea点击:

REST API - DTOs or not?

javaspringrestdtohateoas

提问by benbjo

I am currently creating a REST-API for a project and have been reading article upon article about best practices. Many seem to be against DTOs and simply just expose the domain model, while others seem to think DTOs (or User Models or whatever you want to call it) are bad practice. Personally, I thought that this articlemade a lot of sense.

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

However, I also understand the drawbacks of DTOs with all the extra mapping code, domain models that might be 100% identical to their DTO-counterpart and so on.

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

Our API is mostly created so that other clients may consume data, however if we do it right we would also like to use it for our own web GUI if possible.

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

The thing is that we might not want to expose all the domain data to the other client users. Much of the data will only make sense in our own web application. Also, we might not want to expose all data about an object in all scenarios, especially relationships to other objects and so on. For example, if we expose a list of a particular object we would not necessarily want to expose the entire object hierarchy; so that the object's children will not be exposed, but can be discovered through links (hateoas).

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

How should I go about solving this problem? I was thinking about using Hymanson mixins on our domain models to control what data would be exposed given different scenarios. Or should we just use DTOs all the way - even given its drawbacks and controversy?

我应该如何解决这个问题?我正在考虑在我们的领域模型上使用 Hymanson mixin 来控制在不同场景下会暴露哪些数据。还是我们应该一直使用 DTO——即使考虑到它的缺点和争议?

回答by Martin Frey

As you already stated yourself, this is clearly an opinion related question. I myself am more drawn to the No-DTOs approach, simply because of all the boilerplate code you need.

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

This is mainly true for the response side of a json/rest api. I even wrote a Hymanson addon to avoid writing many json views / filters for these cases: https://github.com/Antibrumm/Hymanson-antpathfilter

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

On the other hand DTOs are a good thing on the request input side of such APIs. Working directly on entities can be pretty hard taking into account bidirectional relations for example. Also you dont really want to let a caller modify a "creator" attribute for example. So you would need to dissallow certain fields during the mapping of such requests.

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

回答by David Siro

When your API is public and you have to support multiple versions, you have to go with DTOs.

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

On the other hand, if it's private API and you control both client and server, I tend to skip the DTOs and expose directly domain model.

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

回答by Argb32

I tend to use DTOs.

我倾向于使用 DTO。

I don't like the drawbacks but it seems that the other options are even worse:

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

Exposition of domain objects may lead to security issues and data leak. Hymanson annotations may seem to solve the problem but it's too easy to make a mistake and expose data which should not to be exposed. When designing a DTO class it's much harder to make such a mistake.

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

On the other side the drawbacks of DTO approach can be reduced with things like object to object mapping and Lombokfor less boilerplate.

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

回答by cassiomolin

Why you should use DTOs in your REST API

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

DTOstands for Data Transfer Object.

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

This pattern was created with a very well defined purpose: transfer data to remote interfaces, just like web services. This pattern fits very well in a REST API and DTOs will give you more flexibilityin the long run.

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

The models that represent the domainof your application and the models that represent the data handled by your APIare (or at least should be) different concernsand should be decoupledfrom each other. You don't want to break your API clients when you add, remove or rename a field from the application domain model.

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

While your service layer operates over the domain/persistence models, your API controllers should operate over a different set of models. As your domain/persistence models evolve to support new business requirements, for example, you may want to create new versions of the API models to support these changes. You also may want to deprecate the old versions of your API as new versions are released. And it's perfectly possible to achieve when the things are decoupled.

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



Just to mention a few benefits of exposing DTOs instead of persistence models:

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

  • Decouplepersistence models from API models.

  • DTOs can be tailoredto your needs and they are great when exposing only a set of attributes of your persistence entities. You won't need annotations such as @XmlTransientand @JsonIgnoreto avoid the serialization of some attributes.

  • By using DTOs, you will avoid a hell of annotationsin your persistence entities, that is, your persistence entities won't be bloated with non persistence related annotations.

  • You will have full controlover the attributes you are receiving when creating or updating a resource.

  • If you are using Swagger, you can use @ApiModeland @ApiModelPropertyannotations to document your API models without messing your persistence entities.

  • You can have different DTOs for each version of your API.

  • You'll have more flexibility when mapping relationships.

  • You can have different DTOs for different media types.

  • Your DTOs can have a list of links for HATEOAS. That's the kind of thing that shouldn't be added to persistence objects. When using Spring HATEOAS, you can make your DTO classes extend RepresentationModel(formerly known as ResourceSupport) or wrap them with EntityModel(formerly known as Resource<T>).

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

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

  • 通过使用 DTO,您将避免持久性实体中的大量注释,也就是说,您的持久性实体不会因​​非持久性相关的注释而变得臃肿。

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

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

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

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

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

  • 您的 DTO 可以拥有HATEOAS的链接列表。这是不应该添加到持久性对象的那种东西。使用Spring HATEOAS 时,您可以使 DTO 类扩展RepresentationModel(以前称为ResourceSupport)或用它们包装EntityModel(以前称为Resource<T>)。

Dealing with the boilerplate code

处理样板代码

You won't need to map your persistence entities to DTOs and vice versa mannually. There are many mapping frameworksyou can use to do it. For instance, have a look at MapStruct, which is annotation based and works as a Maven Annotation Processor. It works well in both CDI and Spring-based applications.

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

You also may want to consider Lombokto generate getters, setters, equals(), hashcode()and toString()methods for you.

您可能还想考虑Lombok为您生成 getter、setter equals()hashcode()toString()方法。



Related:To give better names to your DTO classes, refer to this answer.

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