SpringBoot系列:Pojo validation


 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=) 被注釋的字符串的大小必須在指定的范圍內
  @Email 被注釋的元素必須是電子郵箱地址
  @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精髓>>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM