JSR303參數校驗
系統在登錄的時候做了一個參數校驗,也就是說每一個方法的開頭都要去做一個校驗,那麼有沒有更簡潔的方法呢?那就是使用JSR 303 校驗。
JSR 303 用於對Java Bean 中的字段的值進行驗證,使得驗證邏輯從業務代碼中脫離出來。是一個運行時的數據驗證框架,在驗證之後驗證的錯誤信息會被馬上返回。
- 引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 用法:
在需要驗證的參數前面打上標籤註解@Valid,那麼此註解就會自動對該Bean 進行參數校驗。具體校驗規則在該Bean內部實現。本項目是對登陸時候,利用到了參數校驗。
public class LoginVo {
private String mobile;
private String password;
@NotNull
@IsMobile
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@NotNull
@Length(min=32)//限定密碼的長度
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
好處:可以直接使用@NotNull、@Length(min=32)等註解進行驗證,避免重複的校驗代碼,只需在傳入的參數上打上註解就可以進行參數校驗,避免代碼冗餘。
- 自定義一個@IsMobile驗證器:
- 新建一個註解IsMobile,並且引入相應的規則
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { IsMobileValidator.class })//繼承校驗器
public @interface IsMobile {
boolean required() default true;
String message() default "手機號碼格式有誤!";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
注意:只有註解系統是不會知道的,需要做一些處理。因爲IsMobile裏面需要一個@Constraint(validatedBy = { IsMobileValidator.class }),即IsMobileValidator。繼承一個校驗器。
- 新建一個IsMobileValidator類,需要繼承ConstraintValidator校驗器
public class IsMobileValidator implements ConstraintValidator<IsMobile,String>{
private boolean required=false;
public void initialize(IsMobile constraintAnnotation) {
required=constraintAnnotation.required();
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if(required) {//查看值是否是必須的
return ValidatorUtil.isMobile(value);
}else {
if(StringUtils.isEmpty(value)) {//required
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
}
分析:首先會去找initialize初始化方法,在初始化的時候可以拿到IsMobile註解,然後查看值是否爲必須的,如果是必須的,那麼isValid會進行驗證邏輯。
- 自定義的手機驗證邏輯工具類ValidatorUtil
public class ValidatorUtil {
private static final Pattern mobile_pattern=Pattern.compile("1\\d{10}");//1開頭,然後10個數字,那麼正確的手機號
//驗證手機號格式
public static boolean isMobile(String src) {
if(StringUtils.isEmpty(src)) {
return false;
}
Matcher m=mobile_pattern.matcher(src);
return m.matches();
}
}
全局異常處理
當定義了JSR303校驗器後,校驗不通過都會產生一個BindException( org.springframework.validation.BindException)和一大串錯誤信息(其中就包括校驗的處理信息)。若要對異常處理,我們可以定義一個全局異常處理的攔截器。
好處:可以實現對項目中所有產生的異常進行攔截,在同一個類中實現統一處理。避免異常漏處理的情況。
步驟:
- 新建GlobalExceptionHandler類
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
//攔截什麼異常
@ExceptionHandler(value=Exception.class)//攔截所有的異常
public Result<String> exceptionHandler(HttpServletRequest request,Exception e){
e.printStackTrace();
if(e instanceof GlobalException) {
GlobalException ex=(GlobalException) e;
CodeMsg cm=ex.getCm();
return Result.error(cm);
}
if(e instanceof BindException) {//是綁定異常的情況
//強轉
BindException ex=(BindException) e;
//獲取錯誤信息
List<ObjectError> errors=ex.getAllErrors();
ObjectError error=errors.get(0);
String msg=error.getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
}else {//不是綁定異常的情況
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
注意:
- @ControllerAdvice @ControllerAdvice是一個@Component,用於定義@ExceptionHandler,@InitBinder和@ModelAttribute方法,適用於所有使用@RequestMapping方法,會對所有@RequestMapping方法進行檢查,攔截。並進行異常處理。
- @ExceptionHandler 標註要被攔截的異常,value=Exception.class代表攔截所有的異常,
- @ResponseBody 爲了方便輸出,使得這個GlobalExceptionHandler類裏面的方法跟我們Controller類一樣是輸出的信息,返回值Result類型,可以攜帶信息。當參數校驗不通過的時候,輸出也是Result(CodeMsg),傳給前端用於前端顯示獲取處理。
定義一個全局異常GlobalException,出現異常就可以直接拋這個異常即可。GlobalException()繼承Runtime類,重寫構造函數,傳入CodeMsg 。
/**
* 全局異常處理
*
*/
public class GlobalException extends RuntimeException{
private static final long serialVersionUID = 1L;
private CodeMsg cm;
public GlobalException(CodeMsg cm){
super(cm.toString());
this.cm=cm;
}
public CodeMsg getCm() {
return cm;
}
public void setCm(CodeMsg cm) {
this.cm = cm;
}
}
全局異常處理場景:先檢查異常類型,若是我們業務異常,返回即可。業務中發現異常直接拋出我們自定義的異常即可。
public Result<boolean> login(HttpServletResponse response, LoginVo loginVo) {
if (loginVo == null )
throw new GlobalException(CodeMsg.SERVER_ERROR);
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
//判斷手機號 是否能查到對象
if(!ValidatorUtil.isMobile(mobile)) {//手機號驗證不通過 false
throw new GlobalException(CodeMsg.MOBILE_ERROR);
}
MiaoshaUser miaoshaUser = getById(Long.valueOf(mobile));
if (miaoshaUser == null){
throw new GlobalException(CodeMsg.MIAOSHA_ERROR);
}
return Result.success(true);
}