您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Spring Boot 中怎么利用JSR303 實現參數驗證,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
給參數對象添加校驗注解
@Data public class User { private Integer id; @NotBlank(message = "用戶名不能為空") private String username; @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密碼必須為8~16個字母和數字組合") private String password; @Email private String email; private Integer gender; }
Controller 中需要校驗的參數Bean前添加 @Valid 開啟校驗功能,緊跟在校驗的Bean后添加一個BindingResult,BindingResult封裝了前面Bean的校驗結果。
@RestController @RequestMapping("/user") public class UserController { @PostMapping("") public Result save (@Valid User user , BindingResult bindingResult) { if (bindingResult.hasErrors()) { Map<String , String> map = new HashMap<>(); bindingResult.getFieldErrors().forEach( (item) -> { String message = item.getDefaultMessage(); String field = item.getField(); map.put( field , message ); } ); return Result.build( 400 , "非法參數 !" , map); } return Result.ok(); } }
測試如下:
參數校驗不通過時,會拋出 BingBindException 異常,可以在統一異常處理中,做統一處理,這樣就不用在每個需要參數校驗的地方都用 BindingResult 獲取校驗結果了。
@Slf4j @RestControllerAdvice(basePackages = "com.itwolfed.controller") public class GlobalExceptionControllerAdvice { @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class}) public Result handleVaildException(Exception e){ BindingResult bindingResult = null; if (e instanceof MethodArgumentNotValidException) { bindingResult = ((MethodArgumentNotValidException)e).getBindingResult(); } else if (e instanceof BindException) { bindingResult = ((BindException)e).getBindingResult(); } Map<String,String> errorMap = new HashMap<>(16); bindingResult.getFieldErrors().forEach((fieldError)-> errorMap.put(fieldError.getField(),fieldError.getDefaultMessage()) ); return Result.build(400 , "非法參數 !" , errorMap); } }
新增和修改對于實體的校驗規則是不同的,例如id是自增的時,新增時id要為空,修改則必須不為空;新增和修改,若用的恰好又是同一種實體,那就需要用到分組校驗。
校驗注解都有一個groups屬性,可以將校驗注解分組,我們看下 @NotNull的源碼:
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Repeatable(List.class) @Documented @Constraint(validatedBy = { }) public @interface NotNull { String message() default "{javax.validation.constraints.NotNull.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented @interface List { NotNull[] value(); } }
從源碼可以看出 groups 是一個Class<?>類型的數組,那么就可以創建一個Groups.
public class Groups { public interface Add{} public interface Update{} }
給參數對象的校驗注解添加分組
@Data public class User { @Null(message = "新增不需要指定id" , groups = Groups.Add.class) @NotNull(message = "修改需要指定id" , groups = Groups.Update.class) private Integer id; @NotBlank(message = "用戶名不能為空") @NotNull private String username; @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密碼必須為8~16個字母和數字組合") private String password; @Email private String email; private Integer gender; }
Controller 中原先的 @Valid不能指定分組 ,需要替換成 @Validated
@RestController @RequestMapping("/user") public class UserController { @PostMapping("") public Result save (@Validated(Groups.Add.class) User user) { return Result.ok(); } }
測試如下:
雖然JSR303和springboot-validator 已經提供了很多校驗注解,但是當面對復雜參數校驗時,還是不能滿足我們的要求,這時候我們就需要 自定義校驗注解。
例如User中的gender,用 1代表男 2代表女,我們自定義一個校驗注解 @ListValue,指定取值只能1和2。
@Documented @Constraint(validatedBy = { ListValueConstraintValidator.class }) @Target({ METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) public @interface ListValue { String message() default ""; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; int[] vals() default { }; }
一個標注(annotation) 是通過
@interface關鍵字來定義的. 這個標注中的屬性是聲明成類似方法
的樣式的. 根據Bean Validation API 規范的要求:
message屬性, 這個屬性被用來定義默認得消息模版, 當這個約束條件被驗證失敗的時候,通過
此屬性來輸出錯誤信息。
groups 屬性, 用于指定這個約束條件屬于哪(些)個校驗組. 這個的默認值必須是Class<?>類型數組。
payload 屬性, Bean Validation API 的使用者可以通過此屬性來給約束條件指定嚴重級別. 這個屬性并不被API自身所使用。
除了這三個強制性要求的屬性(message, groups 和 payload) 之外, 我們還添
加了一個屬性用來指定所要求的值. 此屬性的名稱vals在annotation的定義中比較特
殊, 如果只有這個屬性被賦值了的話, 那么, 在使用此annotation到時候可以忽略此屬性名稱.
另外, 我們還給這個annotation標注了一些元標注( meta
annotatioins):
@Target({ METHOD, FIELD, ANNOTATION_TYPE }): 表示此注解可以被用在方法, 字段或者
annotation聲明上。
@Retention(RUNTIME): 表示這個標注信息是在運行期通過反射被讀取的.
@Constraint(validatedBy = ListValueConstraintValidator.class): 指明使用哪個校驗器(類) 去校驗使用了此標注的元素.
@Documented: 表示在對使用了該注解的類進行javadoc操作到時候, 這個標注會被添加到
javadoc當中.
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.HashSet; import java.util.Set; public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> { private Set<Integer> set = new HashSet<>(); /** * 初始化方法 */ @Override public void initialize(ListValue constraintAnnotation) { int[] vals = constraintAnnotation.vals(); for (int val : vals) { set.add(val); } } /** * 判斷是否校驗成功 * * @param value 需要校驗的值 * @param context * @return */ @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return set.contains(value); } }
ListValueConstraintValidator定義了兩個泛型參數, 第一個是這個校驗器所服務到標注類型(在我們的例子中即ListValue), 第二個這個校驗器所支持到被校驗元素的類型 (即Integer)。
如果一個約束標注支持多種類型的被校驗元素的話, 那么需要為每個所支持的類型定義一個ConstraintValidator,并且注冊到約束標注中。
這個驗證器的實現就很平常了, initialize() 方法傳進來一個所要驗證的標注類型的實例, 在本
例中, 我們通過此實例來獲取其vals屬性的值,并將其保存為Set集合中供下一步使
用。
isValid()是實現真正的校驗邏輯的地方, 判斷一個給定的int對于
@ListValue這個約束條件來說
是否是合法的。
在參數對象中使用 @ListValue注解。
@Data public class User { @Null(message = "新增不需要指定id" , groups = Groups.Add.class) @NotNull(message = "修改需要指定id" , groups = Groups.Update.class) private Integer id; @NotBlank(message = "用戶名不能為空") @NotNull private String username; @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密碼必須為8~16個字母和數字組合") private String password; @Email private String email; @ListValue( message = "性別應指定相應的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class}) private Integer gender; }
測試如下:
看完上述內容,你們對Spring Boot 中怎么利用JSR303 實現參數驗證有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。