首先呢,先祝你們新年快樂!!牛年大吉!!html
咱們如今開啓咱們新一年的學習吧。今天,咱們將聊一下在Springboot應用程序中驗證數據的經常使用實現。java
通常實現是經過使用Bean驗證API進行驗證。Bean驗證API的參考實現是Hibernate驗證器。spring
全部必需的依賴項都打包在springbootstarter POM springbootstarter驗證中。所以,一般您只須要開始如下依賴關係:數據庫
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
複製代碼
驗證約束是經過使用適當的Bean驗證註解對字段進行註解來定義的。例如:編程
public class Address {
@NotBlank
@Size(max = 50)
private String street;
@NotBlank
@Size(max = 50)
private String city;
@NotBlank
@Size(max = 10)
private String zipCode;
@NotBlank
@Size(max = 3)
private String countryCOde;
// getters + setters
}
複製代碼
對於這些註解的做用是顯而易見的。咱們將在下面的許多示例中使用到這個Address類。springboot
能夠在Bean驗證文檔中找到內置約束註解的完整列表。markdown
若是有須要,經過建立自定義約束驗證器來定義本身的驗證約束。app
在使用Springboot構建RestAPI接口時,大多時候須要驗證傳入的請求數據。這能夠經過簡單地將@Valid註解添加到@RequestBody方法參數來完成。框架
例如:函數
@RestController
public class AddressController {
@PostMapping("/address")
public void createAddress(@Valid @RequestBody Address address) {
// ..
}
}
複製代碼
經過@Valid註解,就開啓了數據約束驗證。
Spring如今根據先前定義的約束自動驗證傳遞的Address對象。
這種類型的驗證一般用於確保客戶端發送的數據語法正確。若是驗證失敗,則不調用控制器方法,並向客戶端返回HTTP 400(錯誤請求)響應。更復雜的特定於業務的驗證約束一般應該稍後在業務層中檢查。
在Springboot應用程序中使用關係數據庫時,持久層採用了Hibernate框架,也會支持驗證。Hibernate支持Bean驗證。若是實體包含Bean驗證註解,則在持久化實體時會自動檢查這些註解。
請注意,持久層絕對不該該是驗證的惟一位置。若是驗證在這裏失敗,一般意味着其餘應用程序組件中缺乏某種驗證。持久層驗證應該被視爲最後一道防線。除此以外,持久性層對於與業務相關的驗證來講一般爲時已晚。
Spring提供了對於方法參數的數據約束驗證。經過向方法參數添加Bean驗證註解。而後,Spring使用AOP攔截器在調用實際方法以前驗證參數。
例如:
@Service
@Validated
public class CustomerService {
public void updateAddress( @Pattern(regexp = "\\w{2}\\d{8}") String customerId, @Valid Address newAddress ) {
// ..
}
}
複製代碼
另外,這種方法對於驗證進入服務層的數據很是有用。可是,在使用這種方法以前,應該瞭解它的侷限性,由於這種類型的驗證只有在涉及Spring代理時纔有效。
同時須要注意,這種方法會使單元測試變得更困難。
在上述的驗證方案中,實際的驗證是由Spring或Hibernate觸發的。不少時候,咱們須要根據合適時機,靈活觸發Bean驗證。
下面,咱們嘗試用編程方式,觸發對於Bean驗證。
咱們首先建立一個驗證Facade bean:
@Component
public class ValidationFacade {
private final Validator validator;
public ValidationFacade(Validator validator) {
this.validator = validator;
}
public <T> void validate(T object, Class<?>... groups) {
Set<ConstraintViolation<T>> violations = validator.validate(object, groups);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
複製代碼
這個bean接受一個驗證器做爲有參構造函數。驗證器是Bean驗證API的一部分,負責驗證Java對象。
在validate(..)方法中,咱們使用驗證器來驗證傳遞的對象。結果是一組約束衝突。若是未違反任何驗證約束(=對象有效),則集合爲空。不然,咱們拋出一個約束衝突異常。
咱們如今能夠將咱們的驗證門面注入到其餘bean中。例如:
@Service
public class CustomerService {
private final ValidationFacade validationFacade;
public CustomerService(ValidationFacade validationFacade) {
this.validationFacade = validationFacade;
}
public void updateAddress(String customerId, Address newAddress) {
validationFacade.validate(newAddress);
// ...
}
}
複製代碼
爲了驗證一個對象Address(這裏是新地址),咱們只需調用validate(..)方法。固然,咱們也能夠將驗證器直接注入到咱們的客戶服務中。可是,在驗證錯誤的狀況下,咱們一般不但願處理返回的約束衝突集。相反,咱們可能只想拋出一個異常,這正是驗證門面所作的。
一般,這是在服務/業務層進行驗證的好的一種實現。它不只限於方法參數,並且能夠用於不一樣類型的對象。例如,咱們能夠從數據庫中加載一個對象,修改它,而後在繼續以前驗證它。
這種方法對於單元測試也很好,由於咱們能夠簡單地模擬驗證門面。若是咱們想在單元測試中進行真正的驗證,能夠手動建立所需的驗證器實例。
咱們能夠對實際的業務Bean對象進行建立時的動態驗證。
在DDD領域驅動設計開發時,採用動態驗證比較重要。例如,在建立地址實例時,構造函數能夠確保咱們不能構造無效的對象:
public class Address {
@NotBlank
@Size(max = 50)
private String street;
@NotBlank
@Size(max = 50)
private String city;
...
public Address(String street, String city) {
this.street = street;
this.city = city;
ValidationHelper.validate(this);
}
}
複製代碼
在這裏,構造函數調用靜態validate(..)方法來驗證對象狀態。這個靜態validate(..)方法與前面在ValidationFacade中顯示的方法相似:
這裏的區別是,咱們沒有經過Spring檢索驗證程序實例。相反,咱們使用如下方法手動建立:
Validation.buildDefaultValidatorFactory().getValidator()
複製代碼
經過這種方式,咱們能夠直接將驗證集成到域對象中,而無需依賴外部人員來驗證對象。
今天咱們聊了下,在Springboot應用程序中處理驗證的不一樣方法。
不一樣的使用,根據具體的業務須要,咱們實際去作本身的開發策略。
好了,今天聊天結束。新的一年,你們也要好好學習哦!