SpringBoot參數校驗最全使用


引入maven依賴(可選)

如果我們的項目使用了Spring Boot,hibernate validator框架已經集成在 spring-boot-starter-web中,所以無需再添加其他依賴。如果不是Spring Boot項目,則需要添加如下依賴:

1 <dependency>
2     <groupId>org.hibernate.validator</groupId>
3     <artifactId>hibernate-validator</artifactId>
4     <version>6.0.8.Final</version>
5 </dependency>

 

常用注解介紹

注解 作用類型 來源 說明
@Null 任何類型   屬性必須為null
@NotNull 任何類型   屬性不能為null
@NotEmpty 集合 hibernate validator擴展注解 集合不能為null,且size大於0
@NotBlank 字符串、字符 hibernate validator擴展注解 字符類不能為null,且去掉空格之后長度大於0
@AssertTrue Boolean、boolean   布爾屬性必須是true
@Min 數字類型(原子和包裝)   限定數字的最小值(整型)
@Max 同@Min   限定數字的最大值(整型)
@DecimalMin 同@Min   限定數字的最小值(字符串,可以是小數)
@DecimalMax 同@Min   限定數字的最大值(字符串,可以是小數)
@Range 數字類型(原子和包裝) hibernate validator擴展注解 限定數字范圍(長整型)
@Length(min=,max=) 字符串 hibernate validator擴展注解 限定字符串長度
@Size 集合   限定集合大小
@Past 時間、日期   必須是一個過去的時間或日期
@Future 時期、時間   必須是一個未來的時間或日期
@Email 字符串 hibernate validator擴展注解 必須是一個郵箱格式
@Pattern 字符串、字符   正則匹配字符串

 

單參數校驗

controller類上必須添加@Validated注解,如下所示:

 

1 @RestController
2 @RequestMapping("/user")
3 @Validated // 需要添加的注解
4 public class UserController {
5   // do something
6 }

 

方法參數前面加上注解即可, 如下所示:

1 public Result deleteUser(@NotNull(message = "id不能為空") Long id) {
2   // do something
3 }

 

對象參數校驗

對象參數校驗使用時,需要先在對象的校驗屬性上添加注解,然后在Controller方法的對象參數前添加@Validated注解,如下所示:

 1 public Result addUser(@RequestBody @Validated UserDto userDto) {
 2     // do something
 3 }
 4 
 5 public class UserDto {
 6   @NotBlank(message="名稱不能為空")
 7   private String name;
 8 
 9   @NotNull(message="年齡不能為空")
10   private Integer age;
11   
12   ……
13 }

 

嵌套對象校驗

如果需要校驗的參數對象中還嵌套有一個對象屬性,而該嵌套的對象屬性也需要校驗,那么就需要在該對象屬性上增加@Valid注解。

 1 public class UserDto {
 2     @NotNull
 3     private Long id;
 4     
 5     @NotBlank
 6     private String name;
 7 
 8     @NotNull
 9     private Integer age;
10     
11     @Valid
12     private Phone phone;
13     
14     ……
15 }
16 
17 public class Phone {
18     @NotBlank
19     private String type;
20     
21     @NotBlank
22     private String phoneNum;
23 }

 

分組校驗

在對象參數校驗場景下,有一種特殊場景,同一個參數對象在不同的場景下有不同的校驗規則。比如,在創建對象時不需要傳入id字段(id字段是主鍵,由系統生成,不由用戶指定),但是在修改對象時就必須要傳入id字段。在這樣的場景下就需要對注解進行分組。

1)組件默認有個分組Default.class, 所以我們可以再創建一個分組例如UpdateAction.class,如下所示:

1 public interface UpdateAction {
2 }

2)在參數類中需要校驗的屬性上,在注解中添加groups屬性(表示Default.class默認情況下會校驗name,age參數,而在UpdateAction.class情況下會校驗id參數),如下所示:

 1 public class UserDto {
 2     @NotNull(groups = {UpdateAction.class}, message = "id不能為空")
 3     private Long id;
 4     
 5     @NotBlank
 6     private String name;
 7 
 8     @NotNull
 9     private Integer age;
10     
11     ……
12 }

 3)在controller的方法中,在@Validated注解里指定哪種場景即可(沒有指定就代表采用Default.class,采用其他分組則需要顯示指定)。如下代碼便表示在addUser()接口中按照默認情況進行參數校驗,在updateUser()接口中按照默認情況和UpdateAction分組對參數進行共同校驗。

1 public Result addUser(@Validated UserDto userDto) {
2   // do something
3 }
4 
5 public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserDto userDto) {
6   // do something
7 }

 

枚舉校驗

枚舉校驗hibernate validator並不支持。需要自己擴展實現。

1) 定義EnumChck注解,如下所示:

 1 @Documented
 2 @Constraint(validatedBy = EnumConstraintValidator.class)
 3 @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
 4 @Retention(RUNTIME)
 5 @Repeatable(EnumCheck.List.class)
 6 public @interface EnumCheck {
 7     String message() default "{javax.validation.constraints.EnumCheck.message}";
 8 
 9     Class<?>[] groups() default { };
10 
11     Class<? extends Payload>[] payload() default { };
12 
13     /**
14      * 枚舉類
15      *
16      */
17     Class<? extends EnumValidator> clazz();
18 
19     /**
20      * 調用的方法名稱
21      */
22     String method() default "getValue";
23 
24     @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
25     @Retention(RUNTIME)
26     @Documented
27     @interface List {
28         EnumCheck[] value();
29     }
30 }

2) 定義EnumValidator接口,讓需要校驗的枚舉類實現其接口的getValue()方法,如下所示:

1 public interface EnumValidator {
2     Object getValue();
3 }

3)自定義實現EnumConstraintValidator,需要實現ConstraintValidator接口,如下所示:

 1 public class EnumConstraintValidator implements ConstraintValidator<EnumCheck, Object> {
 2     /**
 3      * 注解對象
 4      */
 5     private EnumCheck annotation;
 6 
 7     /**
 8      * 初始化方法
 9      *
10      * @param constraintAnnotation 注解對象
11      */
12     @Override
13     public void initialize(EnumCheck constraintAnnotation) {
14         this.annotation = constraintAnnotation;
15     }
16 
17     @Override
18     public boolean isValid(Object value, ConstraintValidatorContext context) {
19         if (Objects.isNull(value)) {
20             return false;
21         }
22 
23         Object[] enumConstants = annotation.clazz().getEnumConstants();
24         try {
25             Method method = annotation.clazz().getMethod(annotation.method());
26             for (Object enumConstant : enumConstants) {
27                 if (value.equals(method.invoke(enumConstant))) {
28                     return true;
29                 }
30             }
31         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
32             throw new RuntimeException(e);
33         }
34 
35         return false;
36     }
37 }

4) 具體使用步驟如下:

具體枚舉類實現上面定義的EnumValidator接口:

 1 public enum RefNodeType implements EnumValidator {
 2     PROJECT("project", "項目"),
 3     FIELD("field", "變量"),
 4     FUNC("func", "函數");
 5 
 6     private final String code;
 7     private final String label;
 8 
 9     RefNodeType(String code, String label) {
10         this.code = code;
11         this.label = label;
12     }
13 
14     public String getCode() {
15         return code;
16     }
17 
18     public String getLabel() {
19         return label;
20     }
21 
22     @Override // 需要實現getValue方法
23     public Object getValue() {
24         return code;
25     }
26 }

參數校驗加上@EnumCheck枚舉校驗注解,如下所示:

1 public class TestDto {
2   @NotBlank(message = "變量類型不能為空")
3   @EnumCheck(clazz = RefNodeType.class, message = "變量類型不合法") // 加上枚舉校驗注解
4   private String sourceType;
5 }

 

正則通用校驗

1)定義RegRule接口,將需要用到的正則表達式定義為接口屬性,如下所示:

1 public interface RegRule {
2     String MOBILE = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\\d{8})$";
3 }

2)對象屬性加上@Pattern注解,如下所示:

1 public class UserDto {
2   @Pattern(regexp = RegRule.MOBILE, message = "手機號格式不正確")
3   private String mobile;
4 }

 

 各類異常捕獲處理

參數校驗失敗后會拋出異常,我們只需要在全局異常處理類中捕獲參數校驗的失敗異常,然后將錯誤消息添加到返回值中即可。捕獲異常的方法如下所示,返回值Result是我們系統自定義的返回值類。

 1 @RestControllerAdvice
 2 public class GlobalExceptionHandler {
 3      /**
 4       * 全局異常處理
 5       */
 6      @ExceptionHandler(Exception.class)
 7      public Result handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
 8          // do something
 9      }
10 }

1)缺少參數拋出的異常是MissingServletRequestParameterException,如下所示:

1 if (e instanceof MissingServletRequestParameterException){
2     Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
3     String msg = MessageFormat.format("缺少參數{0}", ((MissingServletRequestParameterException) e).getParameterName());
4     result.setMessage(msg);
5     return result;
6 }

2)單參數校驗失敗后拋出的異常是ConstraintViolationException,如下所示:

 1 if (e instanceof ConstraintViolationException){
 2   Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
 3   Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) e).getConstraintViolations();
 4   if(CollectionUtils.isNotEmpty(sets)){
 5     StringBuilder sb = new StringBuilder();
 6     sets.forEach(error -> {
 7                     if (error instanceof FieldError) {
 8                         sb.append(((FieldError)error).getField()).append(":");
 9                     }
10                     sb.append(error.getMessage()).append(";");
11                 });
12     String msg = sb.toString();
13     msg = StringUtils.substring(msg, 0, msg.length() -1);
14     result.setMessage(msg);
15   }
16   return result;
17 }

3)get請求的對象參數校驗失敗后拋出的異常是BindException,如下所示:

 1 if (e instanceof BindException){
 2       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
 3       List<ObjectError> errors = ((BindException) e).getBindingResult().getAllErrors();
 4       String msg = getValidExceptionMsg(errors);
 5       if (StringUtils.isNotBlank(msg)){
 6         result.setMessage(msg);
 7       }
 8       
 9       return result;
10 }
11 
12 private String getValidExceptionMsg(List<ObjectError> errors) {
13   if(CollectionUtils.isNotEmpty(errors)){
14     StringBuilder sb = new StringBuilder();
15     errors.forEach(error -> {
16                     if (error instanceof FieldError) {
17                        sb.append(((FieldError)error).getField()).append(":");
18                     }
19                     sb.append(error.getDefaultMessage()).append(";");
20                 });
21     String msg = sb.toString();
22     msg = StringUtils.substring(msg, 0, msg.length() -1);
23     return msg;
24   }
25   return null;
26 }

4)post請求的對象參數校驗失敗后拋出的異常是MethodArgumentNotValidException,如下所示:

 1 if (e instanceof MethodArgumentNotValidException){
 2       Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
 3       List<ObjectError> errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();
 4       String msg = getValidExceptionMsg(errors);
 5       if (StringUtils.isNotBlank(msg)){
 6         result.setMessage(msg);
 7       }
 8       
 9       return result;
10 }

 

 

 

 


免責聲明!

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



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