在哪个层中验证应该在Spring Boot Rest API中。我有一些模型,端点和DTO。我在DTO中添加了一些@NotNull
和@Size
注释。我在端点中添加了@Valid
注释以及@RequestParam
注释。
但现在我想知道我是否应该在@Entity
课程中进行验证?我觉得这将是一个重复的代码。但我读到一层不应该依赖另一层。
答案 0 :(得分:10)
具有讽刺意味的是,有多少人真正相信验证应该是我们在控制器中所参与的事情,或者是他们与业务代码交换的价值对象,如果需要关注状态验证,则不应该在其他地方。
我们应该始终努力在任何应用程序的多个阶段执行验证。
考虑一下控制器接受您打算用来更改某个服务中的业务实体的值对象,并且该值对象仅包含您打算在更大的实体中操作的字段子集。验证表示层中的值对象,并将其传递给提取实体的服务,从值对象中获取值并将其设置在适当的实体上。也许该服务方法也可以操纵其他字段。
我们有什么保证该实体的状态有效?
虽然我们验证了值对象是有效的,但我们只在提供的字段子集的上下文中验证了那些输入。我们没有验证这些值与实体的其他现有状态一起仍然有效。
尝试防范开发人员错误也很重要。测试用例只能让你到目前为止,我们都同意我们不会在测试中验证每个值组合的有效性。我们经常针对非常具体的案例和情景,并从那里得出结论。
通过不仅将验证应用于我们的演示文稿值对象,还应用于我们的实体,您不仅可以让测试用例专注于广泛的功能验证,还可以保证您的数据存储状态永远不会受到应用程序的影响。
答案 1 :(得分:3)
考虑了一段时间后,我决定最好的方法是在两层上进行验证。我会解释原因。
假设您有一个 User
实体和一个 name
字段,并且您的应用程序业务逻辑要求该字段不能为空。您还具有 UserDTO
和相同的 name
字段。
我假设您对实体和DTO的所有验证都将使用 java.validation
API进行。
如果仅在控制器上进行验证,则可以确保持久保存无效实体,但只能从传入请求中进行。如果您拥有操纵实体的服务,则它可能会在不通知您的情况下将实体保持在无效状态(除非对数据库列进行了空检查)。
然后,您可以考虑:“好吧,我将验证注释从DTO移到实体上,一切都会好起来的”。。好,是的,不是!
如果仅在实体上进行验证,则无论传入请求还是服务层都不会受到影响,但是您可能会遇到性能问题。
根据 Anghel Leonard 书中的 Spring Boot持久性最佳实践,每次从数据库加载实体时,Hibernate都会浪费内存和CPU来维持实体状态在持久性上下文中,即使实体处于“只读模式”。
现在,考虑一下。如果用户名为空,而您仅在实体上对其进行验证,则意味着您:
其中许多操作可能会非常昂贵,而您所做的只是将其扔到垃圾箱中,而如果您早先验证了用户名,则根本无法做任何事情。
因此,我的建议是在两层上进行验证。注释使操作变得如此简单,以至于您甚至没有借口不这样做。甚至可以编写自定义验证器来进行复杂的验证,然后可以在许多其他地方重复使用
此外,这是我提到的书的链接,希望您喜欢: https://www.amazon.com.br/dp/B087WS81RK/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1
答案 2 :(得分:1)
在实体中,您应添加constraints
,要求您的数据处于健康状态,并且所有validation
逻辑应位于DTO
,因为您的RestController
服务{{ 1}} DTOs
负责在映射到实体之前检查验证。