驗證邏輯 (以及我的第一篇文章!)

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

大家好!

這是我上個月加入 Interface21 以來的第一篇文章。我之前的部落格現在已正式停用,我將不再更新它。

那麼,我的第一篇文章的主題是什麼(除了自我介紹之外)? 驗證邏輯。 這不會是一個關於如何在 Spring 框架中執行驗證的逐步解說,而是會討論我個人特別困擾的問題 :)

特別是,我想討論驗證邏輯中應該包含哪些內容。 這似乎是一個不用動腦筋的答案;「驗證指定資料的邏輯」。 好吧,這確實是不用動腦筋的,但請繼續閱讀 :)。 如您所知,Spring 框架透過 ErrorsValidator 介面,為您的驗證提供了一個良好的抽象層。 特別是 Validator 是您將業務特定的驗證規則應用於已填充的網域物件的地方。 Spring 出色的綁定支援負責根據一些輸入更新您的網域模型,而 validator 負責確保已填充的網域模型在語意上是正確的。 那麼我的困擾是什麼? 一次又一次,我一直遇到應用程式允許驗證邏輯從 validator 滲出到控制器(對於 Web 應用程式),甚至更糟的是滲出到中介層。 在人們開始提出異議之前;我並不是說驗證不屬於中介層,我只是說 Validator 是放置驗證邏輯的地方!

最常見的例子是當您新增一個新的實體時,例如使用者。 通常 validator 會執行一些「簡單」的檢查(欄位不為 null,文字欄位長度超過 25 個字元等)。 然後控制器(例如)將呼叫中介層 (userService.add(user)) 並捕獲 DuplicateKeyException(或強型別 DuplicateUserException)例外。 如果拋出此例外,控制器將填充 errors 物件,然後重新顯示表單。

那麼這張圖有什麼問題呢? 很簡單,事實上,現在隱含地使用引發 DuplicateKeyException 來表示驗證失敗! 在此範例中,資料庫 (DB) 正在驗證資料以確保其在插入之前是唯一的,如果不是,則拋出例外。

我的重點(我承認我說得很冗長 ;))是,這一切都是屬於 Validator 的驗證邏輯。 將此唯一性檢查移至 Validator 它所屬的位置(!) 可以帶來許多好處

  • 這是一個更乾淨、更直觀的實作;您在哪裡尋找驗證邏輯? 在 validator 中。
  • validator 實作現在是真正可重複使用的。 以前,validator 實際上只驗證了部分資料。
  • 唯一使用者的單元測試是在驗證單元測試中完成的。 這比單元測試控制器容易得多,後者需要模擬 Errors、HttpServletRequest、HttpServletResponse(實際上這也很容易,但仍然....)等等。 需要模擬以測試 validator 的物件數量是 Errors 物件、DAO 和您的網域模型。
  • Controller 中的 onSubmit 方法現在遵守 SimpleFormController 指定的合約(其中聲明只有在驗證成功時才會呼叫),因此程式碼更乾淨。
這不是火箭科學,但很多人就是不「明白」。 我認為這可能是因為他們將 validator 視為驗證請求參數是否正確的地方。 當然,您在網域模型本身上執行此操作,但那仍然是他們的心態。 事實並非如此。 這一切都是關於應用所有您的業務驗證規則。

注意:有一種論點認為您正在重複您的驗證邏輯;資料庫知道什麼是唯一和什麼不是唯一的,那麼為什麼要在控制器中重複該邏輯? 嗯,重點是您正在重複該邏輯,您正在使用拋出 DuplicateXXXException 來指示驗證失敗,因此這並不是一個真正有效的論點。

還有另一種論點認為,這不能 100% 保證捕獲所有(在本例中)重複的鍵。 這是真的。 在驗證被呼叫之後但在中介層呼叫之前,有一個很小的機會之窗,另一個進程可能會偷偷溜進來並建立唯一的列,但這是一個非常非常小的機會之窗(通常是毫秒),並且無論如何都可能會拋出 OptimisticLockingException。 此外,請考慮資料的性質。 單一唯一實體不太可能由兩個不同的執行緒同時建立。 如果確實發生了,那也沒關係。 讓例外向上滲透到容器,因為現在這確實是一種例外情況。

抱怨結束。

附註:我的其餘部落格文章可能也會一樣冗長 :)

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

取得領先

VMware 提供培訓和認證,以加速您的進展。

瞭解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

瞭解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

檢視全部