SpringBoot秒殺系統實戰10-JSR303參數校驗+全局異常處理

文章目錄

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);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章