Spring Boot MVC 引數校驗
文章目錄
Spring Boot Validate
Spring Boot 支援JSR303/JSR349驗證框架,通過註解實現對引數的校驗,並將校驗結果封裝成BindingResult物件。
常用註解
下面的表格列出常用校驗類及主要功能。這些註解必須配合@Valid或@Validated使用,通過這兩個註解開啟校驗。
檢查型別 | 註解 | 說明 |
---|---|---|
空檢查 | @Null | 驗證物件為null |
@NotNull | 驗證物件不為null | |
@NotEmpty | 驗證物件不為null、且長度>0 | |
@NotBlank | 驗證字串不為null、且最少有一個非空格字元 | |
長度檢查 | @Size(min,max) | 驗證物件長度 |
@Length(min,max) | 驗證字串長度 | |
數值檢查 | @Min(value) | 驗證數字>=value |
@Max(value) | 驗證數字<=value | |
@Digits(integer,fraction) | 驗證數字格式 | |
@Range(min,max) | 驗證數字是否符合[min,max] | |
正則檢查 | @Pattern(regexp) | 驗證字串是否符合正則表示式 |
驗證是否郵箱格式 |
注 :對於長度的校驗基本都支援字串、集合、Map、陣列的長度。
下面是@Valid和@Validated的區別。
@Valid | @Validated | |
---|---|---|
分組校驗 | 不支援 | 支援 |
使用範圍 | 方法、屬性、構造方法、引數 | 類、方法、引數 |
巢狀驗證 | 支援 | 不支援 |
注 :巢狀驗證。JavaBean a中某個屬性型別是JavaBean b,對a進行驗證的同時驗證b。
使用說明
基本使用
以新增使用者為例
新建User類,為name屬性新增@NotBlank註解
@Data
class User {
private Integer id;
@NotBlank
private String name;
}
新增新建使用者介面,新增@Validated註解開啟user引數校驗
@RestController
@RequestMapping("/validate")
public class ValidateController {
@RequestMapping("/addUser")
public Integer addUser(@Validated User user) {
// 假設插入資料庫後,生成id為89757
user.setId(89757);
// 插入成功後返回id
return user.getId();
}
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/addUser
由於name是null,校驗不通過,返回錯誤
瀏覽器輸入 :http://localhost:8080/validate/addUser?name=ly
校驗通過,返回89757
實現分組校驗
同一個JavaBean在不同的場景可能需要不用的校驗規則,例如:新增使用者時id必須是null,修改使用者時id不能是null。每一個校驗註解都有group屬性,通過group屬性可以實現不同方法採用不同的校驗規則。以新建使用者為例
修改User類,新增並指定校驗組
@Data
class User {
// 定義新增校驗組和更新校驗組
public interface Add {}
public interface Update {}
// 新增校驗並指定不同的校驗組
@Null(groups = Add.class)
@NotNull(groups = Update.class)
private Integer id;
// 新增新增和修改校驗組
@NotBlank(groups = {Add.class, Update.class})
private String name;
}
新增修改使用者介面,為新增和修改使用者介面指定校驗組
@RestController
@RequestMapping("/validate")
public class ValidateController {
// 通過group屬性指定新增使用者校驗組
@RequestMapping("/addUser")
public Integer addUser(@Validated(User.Add.class) User user) {
// 同上,略
}
// 通過group屬性執行修改使用者校驗組
@RequestMapping("/updateUser")
public boolean updateUser(@Validated(User.Update.class) User user) {
// 假設修改成功,返回true
return true;
}
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/addUser?id=1&name=ly
由於id不是空,校驗不通過,返回錯誤
瀏覽器輸入 :http://localhost:8080/validate/addUser?name=ly
校驗通過,返回89757
瀏覽器輸入 :http://localhost:8080/validate/updateUser?name=ly
由於id是空,校驗不通過,返回錯誤
瀏覽器輸入 :http://localhost:8080/validate/updateUser?id=1&name=ly
校驗通過,返回true
處理校驗結果
Spring Boot 會將校驗結果封裝成BindingResult物件,可以通過BindingResult引數獲取校驗結果。
新增方法,用於測試校驗結果的獲取
@RestController
@RequestMapping("/validate")
public class ValidateController {
// 其他方法略
@RequestMapping("/handlerBindingResult")
public Object bindingResult(@Validated(User.Add.class) User user, BindingResult bindingResult) {
// 若存在校驗異常則處理
if (bindingResult.hasErrors()) {
StringBuilder errorMessage = new StringBuilder();
// 遍歷全部校驗異常並拼接異常資訊
for (FieldError error : bindingResult.getFieldErrors()) {
errorMessage.append(error.getField()).append(error.getDefaultMessage()).append('\n');
}
return errorMessage.toString();
}
return true;
}
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/handlerBindingResult?id=1
返回處理後的錯誤資訊 :id必須為null name不能為空
瀏覽器輸入 :http://localhost:8080/validate/handlerBindingResult?name=ly
校驗通過,返回true
注 :若方法需要校驗多個引數,例如:method(param1,param2…),需要依次獲取校驗結果,例如 :method(param1,bindingResult1,param2,bindingResult2…)。對於未通過引數注入處理的BindingResult物件,會丟擲異常,可以通過捕獲MethodArgumentNotValidException
和BindException
統一處理,個人習慣在這裡處理。
自定義校驗註解
當現有的校驗註解不滿足業務要求的時候,就需要通過自定義註解來滿足我們自定義的校驗規則。以性別校驗為例,假設1代表男、2代表女,輸入其他都是不合法的,下面說明如何自定義性別校驗註解
新建Sex註解,用於性別引數校驗
@Documented
@Retention(RUNTIME)
// 指定校驗類
@Constraint(validatedBy = SexValidator.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@interface Sex {
// 必須的三個屬性
String message() default "引數值不正確";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 允許值(預設1男2女)。若使用方的性別列舉不同,可以通過該欄位指定校驗通過的值。
int[] allowed() default {1, 2};
}
新增Sex註解校驗類
// 校驗類需要實現ConstraintValidator介面,通過泛型指定被校驗註解型別和被校驗欄位型別
class SexValidator implements ConstraintValidator<Sex, Integer> {
private Set<Integer> allowed = new HashSet<>(2);
// 初始化允許校驗通過的值
@Override
public void initialize(Sex sex) {
for (int i : sex.allowed()) {
allowed.add(i);
}
}
// 執行校驗
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return allowed.contains(value);
}
}
修改User類
@Data
class User {
// 略
// 假設註解使用方的性別列舉分別是0未知、1男、2女
@Sex(allowed = {0, 1, 2}, groups = {Add.class, Update.class})
private Integer sex;
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=4
返回處理後的錯誤資訊 :sex引數值不正確
瀏覽器輸入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=1
校驗通過,返回true
配置校驗提示資訊
classpath下新建ValidationMessages.properties檔案
com.example.validate.sex = :incorrect parameter values
修改Sex類
@Documented
@Retention(RUNTIME)
@Constraint(validatedBy = SexValidator.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@interface Sex {
// 指定配置檔案中訊息
String message() default "{com.example.validate.sex}";
//略
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=4
返回處理後的錯誤資訊 :sex:incorrect parameter values
手動進行校驗
一般用不到,校驗方法如下
注入校驗物件,新增手動校驗測試方法
@RestController
@RequestMapping("/validate")
public class ValidateController {
// 注入校驗物件。可以通過Validation.buildDefaultValidatorFactory().getValidator()獲得。
@Autowired
private Validator validator;
// 其他程式碼略
@RequestMapping("/activeValidate")
public String activeValidate(User user) {
// 使用新增使用者規則校驗使用者物件
Set<ConstraintViolation<User>> validate = validator.validate(user, User.Add.class);
// 處理校驗結果
StringBuilder result = new StringBuilder();
for (ConstraintViolation<User> violation : validate) {
result.append(violation.getPropertyPath()).append(violation.getMessage());
}
return result.toString();
}
}
啟動服務,測試程式碼
瀏覽器輸入 :http://localhost:8080/validate/activeValidate?name=ly
返回處理後的錯誤資訊 :sex:incorrect parameter values