SpringBoot系列(五) 接口校驗(非空、非法字符/特殊字符、長度等)


1、引入pom依賴

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.20.Final</version>
</dependency>

2、實體類

單層校驗

直接在類屬性上使用注解

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
 
public class ClassInfoVo {
    @Pattern(regexp = "^.{0,100}$", message = "班級id最長為100個字符")
    private String classId;

    @Pattern(regexp = "^.{0,100}$", message = "班級名最長為100個字符")
    @NotNull(message = "缺少必要的參數規則name")
    @NotBlank(message = "班級名不能為null,或者只包含空格")
    @NotEmpty(message = "班級name不能為null或者長度為0")
    private String className;
}
嵌套多層

在對象屬性上使用@Valid注解

import javax.validation.Valid;
import java.util.List;
 
public class SchoolInfo {
    private String schoolName;
    private String schoolId;
 
    @Valid
    private List<ClassInfoVo> rules;
}
 
public class ClassInfoVo {
    @Pattern(regexp = "^.{0,100}$", message = "班級id最長為100個字符")
    private String classId;

    @Pattern(regexp = "^.{0,100}$", message = "班級名最長為100個字符")
    @NotNull(message = "缺少必要的參數規則name")
    @NotBlank(message = "班級名不能為null,或者只包含空格")
    @NotEmpty(message = "班級name不能為null或者長度為0")
    private String className;
}

3、控制層

使用@Valid注解

@RestController
@RequestMapping(value = "/school")
public class Controller {
	@Autowired
    private SchoolService schoolService;
 
    @PostMapping(value = "/query")
    public BaseResponse query(@RequestBody @Valid SchoolInfo infoVo) {
        return schoolService.query(infoVo);
    }
}

4、全局異常處理

  • 處理Get請求,拋出BindException異常
  • 處理請求參數格式錯誤 @RequestParam上,拋出ConstraintViolationException異常
  • 處理請求參數格式錯誤 @RequestBody上,拋出MethodArgumentNotValidException
@ControllerAdvice
public class YourProjectNameExceptionAdvice {
    private static final Logger LOGGER = LoggerFactory.getLogger(YourProjectNameExceptionAdvice.class);
 
    /**
     * 校驗錯誤攔截處理
     * 處理Get請求中 使用@Valid 驗證路徑中請求實體校驗失敗后拋出的異常
     *
     * @param exception 錯誤信息集合
     * @return 錯誤信息
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public BaseResponse bindExceptionHandler(BindException exception) {
        LOGGER.error("BindException encountered: {0}", exception);
        String message =
                exception.getBindingResult().getAllErrors().stream()
                        .map(DefaultMessageSourceResolvable::getDefaultMessage)
                        .collect(Collectors.joining());
        return BaseResponse.newError("YourProjectName_BindException", message);
    }
 
    /**
     * 校驗錯誤攔截處理
     * 處理請求參數格式錯誤 @RequestParam上validate失敗后拋出的異常是javax.validation.ConstraintViolationException
     *
     * @param exception 錯誤信息集合
     * @return 錯誤信息
     */
    @ResponseBody
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public BaseResponse constraintViolationHandler(ConstraintViolationException exception) {
        LOGGER.error("ConstraintViolation exception encountered: {0}", exception);
        return BaseResponse.newError("YourProjectName_ConstraintViolation", exception.getMessage());
    }
 
    /**
     * 校驗錯誤攔截處理
     * 處理請求參數格式錯誤 @RequestBody上validate失敗后拋出的異常是MethodArgumentNotValidException異常。
     *
     * @param exception 錯誤信息集合
     * @return 錯誤信息
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public BaseResponse methodArgumentNotValidHandler(MethodArgumentNotValidException exception) {
        LOGGER.error("MethodArgumentNotValid exception encountered: {0}", exception);
        BindingResult bindingResult = exception.getBindingResult();
        StringBuilder sb = new StringBuilder();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(": ").append(fieldError.getDefaultMessage()).append(", ");
        }
        return BaseResponse.newError("YourProjectName_MethodArgumentNotValid", sb.toString());
    }
}

5、如果多個請求參數校驗失敗,則遇到一個校驗失敗就拋出異常,接下來的異常參數不做校驗。

@Configuration
public class WebConfig {
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                //failFast的意思只要出現校驗失敗的情況,就立即結束校驗,不再進行后續的校驗。
                .failFast(true)
                .buildValidatorFactory();
 
        return validatorFactory.getValidator();
    }
 
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
        methodValidationPostProcessor.setValidator(validator());
        return methodValidationPostProcessor;
    }
}

6、校驗時常用注解

JSR303定義的校驗類型

Constraint
詳細信息
@Valid 遞歸的對關聯對象進行校驗,如果關聯對象是個集合或者數組,那么對其中的元素進行遞歸校驗
@Pattern(value) 符合指定的正則表達式(校驗非法字符)
@NotNull 參數不為 null
@NotEmpty 參數不能為null,或者是空
@NotBlank() 參數不能為null,或者被Trim的長度是否大於0(可以用來校驗全為空格字符)
@Min() 一個數字,值大於給定值
@Max() 一個數字,值小於給定值
@Digits (integer, fraction) 一個數字,其值必須在可接受的范圍內

舉例代碼中使用的比較代表的:

@Pattern(regexp = "^[\\u4E00-\\u9FA5\\w\\-]{1,57}$", message = "只能包含中文字符、英文字符、數字、下划線和中橫線,1~57個字符")
 
 
@Pattern(regexp = "^((?!=|\\+|-|@|>|<|%).)((?!>|<|%).){0,127}$", message = "不能以=,+,-或@開頭,不能包含<,>和%字符,最長為128個字符")
 
 
@Pattern(regexp = "^(?:false|true)$", message = "只支持false/true")
 
 
@Pattern(regexp = "^[A-Za-z0-9]{32}$", message = "只能是32位的uuid(只有英文和字母)")
 
 
@Pattern(regexp = "^[\\w-.;]{1,100}$", message = "最長為100,只能包含英文、數字、“-”、“_”、“;”")
 
 
@Pattern(
        regexp = "^[/][/\\w-.]{1,254}$",
        message = "須以“/”開頭,只能包含字母、數字、“/”、“_”、“-”和“.”," + "長度不少於2位,長度最長為255")
 
 
@Pattern(regexp = "^(?:A|B)$", message = "類型必須為A或者B")
 
 
@Max(value = 2_140_000_000, message = "只能大於0小於2140000")
@Min(value = 0, message = "只能大於0小於2140000")
 
 
@Max(value = 5000, message = "分頁大小在【1,5000】之間")
@Min(value = 1, message = "分頁大小在【1,5000】之間")
 
 
@Digits(integer = 1, fraction = 0, message = "只能為0或1")
@Max(value = 1, message = "只能為0或1")
@Min(value = 0, message = "只能為0或1")
 
@NotEmpty(message = "缺少必要的參數,數組classIds不能為null,或者長度為0")
    private List<
                    @Pattern(regexp = "^[a-zA-Z\\d]{32}$", message = "id僅由字母和數字組成,且長度為32個字符")
                    @NotEmpty(message = "id不能為null或者為空") String>
            classIds;

這些只是包含一些基本的校驗,負責校驗可以使用是定義校驗注解的方式,主要是要實現ConstraintValidator的處理類。(不多介紹)

7、自定義注解

自定義注解名稱

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ParamValidator.class)
public @interface RetryTimes {
    String message() default "重試次數只能為0,5,10,20,50,100";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
}

同時實現ConstraintValidator類

public class ParamValidator implements ConstraintValidator<RetryTimes, Short> {
    private static final Pattern PATTERN = Pattern.compile("^(?:0|5|10|20|50|100)$");
 
    @Override
    public boolean isValid(Short retryTimes, ConstraintValidatorContext constraintValidatorContext) {
        return PATTERN.matcher(retryTimes.toString()).matches();
    }
}

定義后,如何使用,直接在屬性上添加該注解

@RetryTimes
private short retryTimes;

8、分組校驗(后面補充)

 


免責聲明!

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



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