在表單提交時,controller都要驗證從前端接受的參數,假如每個參數都手動判斷會比較麻煩,可以采用驗證框架來簡化對參數的驗證
1 導入依賴
<!--JSR303參數校驗開始--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!--JSR303參數校驗結束-->
2 controller層方法形參添加Valid注解
@RequestMapping("/do_login") @ResponseBody public Result<String> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) { log.info(loginVo.toString()); //登錄 String token = userService.login(response, loginVo); return Result.success(token); }
對請求參數的封裝pojo
這里的NotNull 是框架自帶的注解,IsMobile是自定義注解
public class LoginVo { @NotNull @IsMobile private String mobile; @NotNull @Length(min=6,max=16,message = "密碼長度錯誤") private String password; public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "LoginVo [mobile=" + mobile + ", password=" + password + "]"; } }
安裝下面的方式自定義注解
@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 { }; }
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)) { return true; }else { return ValidatorUtil.isMobile(value); } } } }
3 全局異常捕獲
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; } }
@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; return Result.error(ex.getCm()); }else 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); } } }
4 service層方法
public String login(HttpServletResponse response, LoginVo loginVo) { if(loginVo == null) { throw new GlobalException(CodeMsg.SERVER_ERROR); } String mobile = loginVo.getMobile(); String formPass = loginVo.getPassword(); //判斷手機號是否存在 MiaoshaUser user = getById(Long.parseLong(mobile)); if(user == null) { throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } //驗證密碼 String dbPass = user.getPassword(); String saltDB = user.getSalt(); String calcPass = MD5Util.formPassToDBPass(formPass, saltDB); if(!calcPass.equals(dbPass)) { throw new GlobalException(CodeMsg.PASSWORD_ERROR); } //生成cookie String token = UUIDUtil.uuid(); addCookie(response, token, user); return token; }
5 返回給前端的json
public class Result<T> { private int code; private String msg; private T data; /** * 成功時候的調用 * */ public static <T> Result<T> success(T data){ return new Result<T>(data); } /** * 失敗時候的調用 * */ public static <T> Result<T> error(CodeMsg codeMsg){ return new Result<T>(codeMsg); } private Result(T data) { this.data = data; } private Result(int code, String msg) { this.code = code; this.msg = msg; } private Result(CodeMsg codeMsg) { if(codeMsg != null) { this.code = codeMsg.getCode(); this.msg = codeMsg.getMsg(); } } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
這樣的好處是對參數校驗只要添加注解就可以了。參數校驗不通過會拋出BindException異常,BindException被全局異常處理器捕獲,將錯誤信息返回給前端。
而且異常處理器也會捕獲自定義的異常。
BindException