JSR 303 規范了bean validation, Hibernate validator實現了JSR 303所有的規范, 同時也是最常用的validator 工具包.
使用 Hibernate validator 可以大大簡化數據驗證工作. 對於 Web 項目, 通常前端需要做一些輸入驗證, 完善的前端驗證能給用戶很好的使用體驗, 但僅有前端驗證還是不夠的, 后端必須也有相應的驗證, 因為前端驗證是很容易跳過去的.
有兩個數據驗證的做法:
方式1. Pojo 上加檢查規則, Controller 師徒函數Pojo形參前加上 @Validated 來觸發檢查動作.
方式2. 在 bean 類(通常是 Service 類) 上加 @Validated注解, 在bean類的方法的形參前增加檢查規則, 然后在controller方法中, 通過調用該bean方法將觸發檢查動作.
===================================
方式1
===================================
使用 Hibernate validator 的步驟:
1. 在 Pojo 類的字段上, 加上 Hibernate validator 注解
2. 在Controller 函數的形參前加上 @Valid 或 @Validated 注解, 觸發一次validation.
3. 在每個 @Valid 或 @Validated 注解參數后, 緊跟一個 BindingResult 類型的參數. 如果沒有提供對應的 BindingResult 參數, Spring MVC 將拋出異常.
4. 在Controller 函數中, 通過 BindingResult 類型的參數獲取檢驗的結果.
--------------------------------------------------------------------
Pojo validation 規則注解
--------------------------------------------------------------------
參考: https://blog.csdn.net/danielzhou888/article/details/74740817
@Null | 被注釋的元素必須為 null | |
@NotNull | 被注釋的元素必須不為 null | |
@NotBlank | 檢查約束字符串是不是Null還有被Trim的長度是否大於0,只對字符串,且會去掉前后空格. | |
@NotEmpty | 檢查約束元素不能為NULL或者是EMPTY. | |
@AssertTrue | 被注釋的元素必須為 true | |
@AssertFalse | 被注釋的元素必須為 false | |
@Min(value) | 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 | |
@Max(value) | 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 | |
@DecimalMin(value) | 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 | |
@DecimalMax(value) | 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 | |
@Size(max, min) | 注釋的元素的長度必須在指定的范圍內, 支持字符串和集合 | |
@Length(min=, max=) | 被注釋的字符串的大小必須在指定的范圍內 | |
被注釋的元素必須是電子郵箱地址 | ||
@Digits (integer, fraction) | 被注釋的元素必須是一個數字,其值必須在可接受的范圍內 | |
@Range(min=, max=) | 被注釋的元素必須在合適的范圍內 | |
@Past | 被注釋的元素必須是一個過去的日期 | |
@Future | 被注釋的元素必須是一個將來的日期 | |
@Pattern(value) | 被注釋的元素必須符合指定的正則表達式 | |
@URL(protocol=, host=, port=, regexp=, flags=) |
被注釋的字符串必須是一個有效的url | |
@CreditCardNumber | 被注釋的字符串必須通過Luhn校驗算法, 銀行卡,信用卡等號碼一般都用Luhn 計算合法性 |
|
--------------------------------------------------------------------
@Valid 和 @Validated 注解
--------------------------------------------------------------------
@Valid 和 @Validated 都用來觸發一次校驗, @Valid 是 JSR 303規范的注解, @Validated 是Spring 加強版的注解, 加強的地方有:
1. @Validated 可以設定條件group, 即可以設定校驗的條件.
比如對於 Book 類中 bookId 是自增id, 在新增 Book 時候要求為 null, 在更新時要求不能是 null. 在實際編碼中, 通常使用幾個空的interface 作為 group 參數.
2. @Validated 支持組序列, 該特性用的較少, 參考 https://blog.csdn.net/wangpeng047/article/details/41726299
所以, 推薦使用 @Validated 注解.
===================================
方式2
===================================
在 bean 類(通常是 Service 類) 上加 @Validated注解, 在bean類的方法的形參前增加檢查規則, 然后在controller方法中, 通過調用該bean方法將觸發檢查動作. 如果檢查結果失敗, 將拋出 ConstraintViolationException 異常.
Service @Validated public class SomeService { @Length(min = 3, max = 5) public String createUser(@NotBlank @Email String email, @NotBlank String username, @NotBlank String password) { return username; } } @RestController @RequestMapping("/") public class SomeController { @Autowired SomeService someService; @GetMapping("/") @RequestMapping("/") public hello(){ someService.createUser("email","username","password"); return "Hello"; } }
====================================
自定義校驗規則
====================================
Hibernate invalidator提供的驗證規則應該滿足大多數情形, 如果願意, 我們也可以自定義自己的驗證規則, 自定義一個校驗規則需要定義一個注解和一個校驗類, 比如要新增一個加班時長的校驗規則.
使用的代碼示例:
@WorkOverTime(max=2)
Integer workOverTime;
@WorkOverTime 注解定義的寫法:
直接寫一個注解語法還是挺難的, 可修改一個已有規則的代碼, 比如 @Email. 修改調整的地方有:
1. 在 @interface WorkOverTime 代碼中, 設定 @Constraint 參數, @Constraint(validatedBy = { WorkOverTimeValidator.class })
2. 增加 max 屬性, 示例代碼如下:
int max() default 5;
WorkOverTimeValidator 類的寫法:
WorkOverTimeValidator 類應該實現 ConstraintValidator 接口, 該接口是泛型接口.
class WorkOverTimeValidator implements ConstraintValidator<WorkOverTime, Integer>{
}
====================================
示例代碼
====================================
----------------------------------
pom.xml
----------------------------------
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
----------------------------------
validation group 接口
----------------------------------
定義三個 validation 分組 group 接口, 用來區分新增/更新/刪除操作.
interface AddEntity { } interface UpdateEntity { } interface DeleteEntity { }
----------------------------------
POJO 類
----------------------------------
public class Book { @Null(message = "bookId should be null when add", groups = { AddEntity.class }) @NotNull(message = "bookId should be not null when update and delete", groups = { UpdateEntity.class, DeleteEntity.class }) private Integer bookId; private String author; public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
----------------------------------
Controller 類
----------------------------------
@RestController @RequestMapping("/bookApi") class BookController { // 新增操作 // Book 參數要加上 @RequestBody // book 參數加上 @Validated 驗證指令 @PostMapping("/books") public Book addBook(@Validated({ AddEntity.class }) @RequestBody Book book, BindingResult bookBinding) { if (bookBinding.hasErrors()) { List<ObjectError> errors = bookBinding.getAllErrors(); errors.stream() .map(e -> e.getObjectName() + "," + e.getDefaultMessage()) .forEach(System.out::println); return null; } book.setBookId(100); return book; } // 更新操作 @PutMapping("/books/{bookId}") public Book updateBook(@Validated({ UpdateEntity.class }) @RequestBody Book book, BindingResult bookBinding) { if (bookBinding.hasErrors()) { List<ObjectError> errors = bookBinding.getAllErrors(); errors.stream() .map(e -> e.getObjectName() + "," + e.getDefaultMessage()) .forEach(System.out::println); return null; } return book; } // list all 操作 @GetMapping("/books") public List<Book> listBooks() { List<Book> books = new ArrayList<Book>(); Book book; book = new Book(); book.setBookId(20); books.add(book); return books; } }
====================================
演示截圖
====================================
1. Post 請求 http://localhost:8080/bookApi/books, 輸入的 Book對象包含 bookId, 輸入截圖
服務器端報錯截圖:
1. Post 請求 http://localhost:8080/bookApi/books, 輸入的 Book對象不包含 bookId, 驗證通過.
====================================
參考
====================================
主要參考李家智的書 <<Spring Boot 2精髓>>