1. 概述
本教程將討論 Java 應用程序的服務層中 Spring 驗證的內容。雖然 Spring Boot 支持與自定義驗證器無縫集成,但執行驗證的標準做法是使用 Hibernate Validator。
在此,我們將學習如何將驗證邏輯從控制器移動到單獨的服務層,並在 Spring 應用程序中實現服務層中的驗證。
2. 應用層架構
根據需求,Java 業務應用程序可以呈現多種不同的形態和類型。例如,我們需要根據這些標準確定我們的應用程序需要哪些層。除非有明確的需求,許多應用程序都不會從服務層或存儲層帶來的複雜性和維護成本中受益。
我們可以通過使用多層來滿足這些要求。這些層包括:
消費者層(或 Web 層)是 Web 應用程序的最上層。 它負責解釋用户的輸入並提供適當的響應。 其他層拋出的異常也必須由 Web 層處理。 由於 Web 層是應用程序的入口點,因此它負責身份驗證並作為防止未經授權用户的第一道防線。
Web 層之下是服務層。它充當事務邊界,並託管應用程序和服務和基礎設施服務。 此外,服務層的公共 API 由應用程序服務提供。 它們通常充當事務邊界並負責授權事務。 基礎設施服務提供連接到外部工具(包括文件系統、數據庫和電子郵件服務器)的“管道代碼”。 許多應用程序服務通常使用這些方法。
Web 應用程序的最低層是持久化層。 換句話説,它負責與用户的存儲數據進行交互。
3. 服務層中的驗證
服務層是在應用程序中用於促進控制器與持久性層之間通信的一層。此外,業務邏輯也存儲在服務層中,其中包含驗證邏輯。模型狀態用於在控制器和服務層之間進行通信。
將驗證視為業務邏輯既有優點也有缺點,Spring 的驗證(和數據綁定)架構並不排除任何一種方案。 特別是,驗證不應綁定到 Web 層,應保持簡單易本地化,並允許使用任何可用的驗證器。
此外,客户端輸入數據不一定通過 REST 控制器流程,如果我們不以服務層進行驗證,則不當的數據可能會通過,從而導致一系列問題。在此情況下,我們將使用標準的 Java JSR-303 驗證方案。
4. 示例
讓我們考慮一下使用 Spring Boot 開發的簡單用户賬户註冊表單。
4.1. 簡單領域模型
首先,我們將僅包含名稱、年齡、電話和密碼這四個屬性:
public class UserAccount {
@NotNull(message = "Password must be between 4 to 15 characters")
@Size(min = 4, max = 15)
private String password;
@NotBlank(message = "Name must not be blank")
private String name;
@Min(value = 18, message = "Age should not be less than 18")
private int age;
@NotBlank(message = "Phone must not be blank")
private String phone;
// standard constructors / setters / getters / toString
}在此類中,我們使用了四種註解——<em @NotNull</em>、<em @Size</em>、<em @NotBlank</em> 和 <em @Min</em>——以確保輸入屬性既不為null也不為空,並且符合大小要求。
4.2. 在服務層中實現驗證
有許多驗證解決方案可供選擇,Spring 或 Hibernate 處理實際的驗證。 另一方面,手動驗證也是一種可行的替代方案。 當涉及到將驗證集成到應用程序的正確部分時,這為我們提供了很大的靈活性。
接下來,讓我們在服務類中實現我們的驗證:
@Service
public class UserAccountService {
@Autowired
private Validator validator;
@Autowired
private UserAccountDao dao;
public String addUserAccount(UserAccount useraccount) {
Set<ConstraintViolation<UserAccount>> violations = validator.validate(useraccount);
if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<UserAccount> constraintViolation : violations) {
sb.append(constraintViolation.getMessage());
}
throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
}
dao.addUserAccount(useraccount);
return "Account for " + useraccount.getName() + " Added!";
}
}Validator 是 Bean Validation API 的一部分,負責驗證 Java 對象。 此外,Spring 自動提供一個 Validator 實例,我們可以將其注入到我們的 UserAccountService 中。 Validator 用於在 validate(..) 函數中驗證傳遞的對象。 結果是一個 Set 中的 ConstraintViolation。
如果未違反任何驗證約束(對象有效),則 Set 為空。 否則,我們拋出 ConstraintViolationException。
4.3. 實現 REST 控制器
接下來,我們將構建 Spring REST 控制器類,以便將服務呈現給客户端或最終用户,並對應用程序的輸入驗證進行評估:
@RestController
public class UserAccountController {
@Autowired
private UserAccountService service;
@PostMapping("/addUserAccount")
public Object addUserAccount(@RequestBody UserAccount userAccount) {
return service.addUserAccount(userAccount);
}
}我們沒有在上述 REST 控制器形式中使用 @Valid 註解來防止任何驗證。
4.4. 測試 REST 控制器
現在,讓我們通過運行 Spring Boot 應用程序來測試此方法。之後,使用 Postman 或任何其他 API 測試工具,我們將向 localhost:8080/addUserAccount 網址發送 JSON 輸入:
{
"name":"Baeldung",
"age":25,
"phone":"1234567890",
"password":"test",
"useraddress":{
"countryCode":"UK"
}
}<div>在確認測試運行成功後,我們現在將檢查驗證是否按預期工作。 下一步的邏輯是使用少量無效輸入測試應用程序。 因此,我們將更新輸入 JSON 以包含無效值:</div>
<div>
{
"name":"",
"age":25,
"phone":"1234567890",
"password":"",
"useraddress":{
"countryCode":"UK"
}
}<div>
我們將收到帶有 HTTP 狀態碼 400 的響應,表明請求無效,並顯示以下錯誤消息。因此,<strong >我們可以看到 Validator 的使用對於驗證的重要性</strong>:
</div
><div
>
Error occurred: Password must be between 4 to 15 characters, Name must not be blank<div>
</div>
5. 優缺點
在服務/業務層,這通常是一種有效的驗證方法。它不限於方法參數,並且可以應用於各種對象。例如,我們可以從數據庫中加載一個對象,對其進行修改,然後在繼續之前對其進行驗證。
我們還可以使用這種方法進行單元測試,以便實際模擬 Service 類。 為了在單元測試中促進真實的驗證,我們可以手動生成必要的 Validator 實例。
無論哪種情況,在測試中都不需要啓動 Spring 應用上下文。
6. 結論
在本快速教程中,我們探討了 Java 業務應用程序的不同層級。我們學習瞭如何將驗證邏輯從控制器中移動到單獨的服務層。此外,我們還實施了一種在 Spring 應用程序的服務層中執行驗證的方法。