Spring Rest API 验证应该在 DTO 中还是在实体中?

Posted

技术标签:

【中文标题】Spring Rest API 验证应该在 DTO 中还是在实体中?【英文标题】:Spring Rest API validation should be in DTO or in entity? 【发布时间】:2022-01-19 00:56:43 【问题描述】:

验证应该在 Spring Boot Rest API 中的哪一层。我有一些模型、端点和 DTO。我在 DTO 中添加了一些 @NotNull@Size 注释。我在端点中添加了@Valid 注释以及@RequestParam 注释。

但现在我想知道是否也应该在 @Entity 类中进行验证?我觉得这将是代码的重复。但我读到,一个层级永远不应该依赖另一个层级。

【问题讨论】:

验证应该只在表现层(DTO)中进行,实体永远不应该到达表现层。 所以实体中没有限制注释?例如,当您创建没有 ORM 的数据库时,您指定 VARCHAR(30)。这限制了字符串长度。 【参考方案1】:

具有讽刺意味的是,有多少人真正相信验证应该是我们在控制器或它们与业务代码交换的值对象中参与的事情,而在其他任何地方都不应该担心状态验证。

我们应该始终努力在任何应用程序的多个阶段执行验证。

现在考虑一个控制器,它接受一个值对象,您打算使用该值对象来更改某些服务中的业务实体,并且该值对象仅包含您打算在更大实体中操作的字段子集。您验证表示层中的值对象并将其传递给您的服务,该服务获取实体,从值对象中获取值并将它们设置在适当的实体上。也许那个服务方法也操纵其他领域。

我们有什么保证该实体的状态是有效

虽然我们验证了值对象是有效的,但我们仅在提供的字段子集的上下文中验证了这些输入。我们没有验证这些值与实体的其他现有状态是否仍然有效。

尝试防止开发人员错误也很重要。测试用例只能让您到目前为止,我们都同意我们不会验证测试中每个值组合的有效性。我们经常针对非常具体的案例和场景,并从中得出结论。

通过不仅对我们的表示值对象而且对我们的实体应用验证,您不仅可以让您的测试用例专注于广泛的功能验证,还可以保证您的数据存储状态不会受到应用程序的影响。

【讨论】:

【参考方案2】:

考虑了一段时间后,我决定最好的方法是在两个层上都进行验证。我会解释原因。

假设您有一个带有 name 字段的 User 实体,并且您的应用程序业务逻辑要求它不能为空。您还有一个 UserDTO 具有相同的 name 字段。

我假设您对实体和 DTO 的所有验证都将使用 java.validation API 进行。

如果您仅在控制器上进行验证,那么您可以安全地避免持久化无效实体,但只能来自传入请求。如果您有一个操作实体的服务,它可能会在您不注意的情况下将实体保持在无效状态(除非对数据库列进行空检查)。

然后,你可以想:“好的,我将验证注释从 DTO 移动到实体,一切都会好起来的”。嗯,是的,不是的!

如果您仅在实体上进行验证,您将不会受到传入请求和服务层的影响,但是您可能会遇到性能问题。

根据Anghel Leonard在其Spring Boot Persistence Best Practices一书中的说法,每次从数据库加载实体时,Hibernate 都会浪费内存和 CPU 来维护实体状态在持久化上下文中,即使实体处于“只读模式”。

现在,考虑一下。如果用户名为 null 并且您仅在实体上验证它,这意味着您:

    开始交易 已加载实体 更改了实体 刷新持久性上下文 回滚交易

其中许多操作可能代价高昂,而您所做的一切只是为了将其扔到垃圾箱中,而如果您之前验证了用户名,您可能根本就不会做任何事情。

所以,我的建议是在两个层上都进行验证。注释使您变得如此简单,您甚至没有借口不这样做。即使是复杂的验证也可以编写自定义验证器,然后可以在许多其他地方重用

另外,这里是我提到的书的链接,希望你喜欢: https://www.amazon.com.br/dp/B087WS81RK/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

【讨论】:

【参考方案3】:

在实体中,您应该添加constraints,这要求您的数据处于健康状态,并且所有validation 逻辑都应该在DTO 中,因为您的RestController 服务于DTOs,这是@ 的责任987654326@ 在映射到实体之前检查验证。

【讨论】:

Sushil,在将 dto 映射到实体后,可能还需要对实体进行修改。因此,验证实体也是明智的。此外,服务层不想假设它从控制器获得的东西是一个有效的实体。

以上是关于Spring Rest API 验证应该在 DTO 中还是在实体中?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot,决定为 REST 和 JPA 分别创建 DTO 对象

如何在Spring Boot Rest API中的BeanUtils.copyProperties中将String转换为枚举

Grails 2.5.4 / Spring Security Rest 1.5.4 - 验证令牌

在使用spring执行rest api之前有啥方法可以验证令牌

如何在 Grails 中使用 spring security rest 插件进行身份验证

Spring Boot 微服务 REST API 安全