验证逻辑(以及我的第一篇文章!)

工程 | Colin Yates | 2006年8月25日 | ...

大家好!

这是我上个月加入 Interface21 以来发布的第一篇文章。我之前的博客现在已正式弃用,我将不再更新它。

那么我第一篇文章的主题是什么(除了自我介绍)?  验证逻辑。  这不会是关于如何在 Spring 框架中执行验证的演练,而是会讨论我一个特别恼人的问题:)

我特别想讨论的是,验证逻辑中到底应该包含什么。这似乎是一个显而易见的答案;“用于验证指定数据的逻辑”。好的,这确实是显而易见的,但请继续阅读 :)。如您所知,Spring 框架通过 Errors Validator 接口为您提供了一个漂亮的验证抽象层。特别是,Validator 是您将业务特定验证规则应用于已填充的域对象的地方。Spring 卓越的绑定支持负责根据某些输入更新您的域模型,而验证器负责确保已填充的域模型在语义上是正确的。那么我的困扰是什么呢?我一次又一次地遇到一些应用程序,它们允许验证逻辑从验证器中流出,进入控制器(对于 Web 应用程序),甚至更糟的是进入中间层。在人们开始提出异议之前;我并不是说验证不属于中间层,我只是说 Validator 是放置验证逻辑的地方!

最常见的例子是添加一个新实体,比如一个用户。通常,验证器会执行许多“简单”检查(字段不能为空,文本字段长度超过 25 个字符等)。控制器(例如)然后会调用中间层(userService.add(user))并捕获 DuplicateKeyException(或强类型的 DuplicateUserException)异常。如果抛出此异常,控制器将填充错误对象并重新显示表单。

那么这张图有什么问题呢?很简单,事实是某些验证现在隐式地通过抛出 DuplicateKeyException 来完成,表明验证失败了!。DB(在此示例中)在插入数据之前验证数据以确保其唯一性,如果不唯一则抛出异常。

我的观点(我承认我说的很冗长 ;))是,这都是属于 Validator 的验证逻辑。将这种唯一性检查移到验证器中,它本就属于那里(!),可以带来许多好处:

  • 这是一个更清晰、更直观的实现;您会在哪里寻找验证逻辑?在验证器中。
  • 验证器实现现在是真正可重用的。以前,验证器实际上只验证了部分数据。
  • 对唯一用户进行单元测试是在验证单元测试中完成的。这比单元测试控制器容易得多,后者需要模拟 Errors、HttpServletRequest、HttpServletResponse(这实际上相当容易,但仍然……)等等。测试验证器需要模拟的对象是 Errors 对象、DAO 和您的域模型。
  • 控制器中的 onSubmit 方法现在遵循 SimpleFormController 规定的契约(即它只在验证成功时才被调用),因此代码更加清晰。
这不是什么高深莫测的事情,但很多人就是不“明白”。我认为这可能是因为他们认为验证器是验证请求参数是否正确的地方。当然,您会在域模型本身上这样做,但这仍然是他们的思维方式。事实并非如此。这完全是关于应用所有您的业务验证规则。

注意:有一种观点认为您正在重复验证逻辑;数据库知道什么是唯一和不唯一的,那么为什么要将该逻辑复制到控制器中呢?好吧,关键是您确实在重复该逻辑,您正在使用抛出 DuplicateXXXException 来指示验证失败,所以这并不是一个真正有效的论点。

还有另一种观点认为,这不能 100% 保证捕获所有(在这种情况下)重复键。这是事实。在调用验证之后但在进行中间层调用之前,有一个很小的机会窗口,另一个进程可能会潜入并创建唯一行,但这是一个非常非常小的窗口(通常是毫秒),而且无论如何都可能会抛出 OptimisticLockingException。还要考虑数据的性质。两个不同的线程同时创建单个唯一实体的情况是极不可能的。如果确实发生了,没关系。让异常向上冒泡到容器,因为它现在确实是一个异常情况。

抱怨结束。

附注:我剩下的博客文章可能也会同样冗长 :)

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有