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

2022-09-02 20:12:13

验证应在哪个层中,位于 Spring Boot Rest API 中。我有一些模型,端点和DTO。我在 DTO 中添加了一些注释。我在终结点中添加了注释以及注释。@NotNull@Size@Valid@RequestParam

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


答案 1

具有讽刺意味的是,有多少人真正相信验证应该是我们在控制器或他们与业务代码交换的价值对象中参与的东西,并且在其他地方不应该关注状态验证。

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

暂时考虑一个控制器,它接受您打算用于更改某些服务中的业务实体的值对象,并且该值对象仅包含您打算在较大实体中操作的字段的子集。在表示层中验证值对象,并将其传递给服务,该服务提取实体,从值对象中获取值并将其设置在相应的实体上。也许该服务方法也操作其他字段。

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

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

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

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


答案 2

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

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

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

如果仅在控制器上进行验证,则可以安全地防止保留无效实体,但只能保留传入请求。如果您有一个操作实体的服务,它可能会将实体保留在无效状态,而您不会注意到(除非对数据库列进行空检查)。

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

如果仅在实体上进行验证,则对于传入请求和服务层,您将受到保护,但是您可能会遇到性能问题。

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

现在,想想看。如果用户名为 null,并且您仅在实体上对其进行验证,则意味着您:

  1. 已启动事务
  2. 已加载实体
  3. 已更改实体
  4. 刷新了持久性上下文
  5. 已回滚事务

其中许多操作可能很昂贵,您所做的一切都只是为了将其扔到垃圾箱上,而如果您之前验证了用户名,则可能永远不会做任何事情。

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

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


推荐