全局统一异常处理+JS303校验+断言工具
摘要:
为了不在Controller层写大量的try-catch代码,需要对异常进行全局统一处理,同时将错误信息和错误码进行统一管理,使用枚举类封装错误码,自定义异常类,创建一个全局异常处理类,在类上打上注解@RestControllerAdvice和Commpont,在方法上打上注解@ExceptionHandler捕捉具体的异常,通过JS303对参数进行校验,避免使用大量的if语句,通过断言工具类,避免手动抛出异常
流程:
自定义异常——定义全局异常捕获类——定义错误码枚举类——控制层通过断言工具抛出异常,传入枚举错误码——自定义异常通过有参构造拿到错误码——被全局异常捕获——返回JSON格式的错误信息与错误码
一:自定义异常——继承RuntimeException类
package cn.ybl.exception;
import cn.ybl.enums.GlobalErrorCode;
import lombok.Data;
/**
* 全局自定义异常类
*/
@Data
public class GlobalCustomException extends RuntimeException{
//异常信息
private String errorMessage;
//异常码
private String errorCode;
public GlobalCustomException() {
super();
}
public GlobalCustomException(String message){
super(message);
}
public GlobalCustomException(String errorMessage,String errorCode){
super(errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public GlobalCustomException(GlobalErrorCode globalErrorCode){
super(globalErrorCode.getMessage());
this.errorCode = globalErrorCode.getCode();
this.errorMessage = globalErrorCode.getMessage();
}
}
二:定义错误码枚举类
package cn.ybl.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public enum GlobalErrorCode {
//成功
OK("0","服务正常"),
//失败
ERROR("-1","系统错误,请稍后重试"),
//公共异常
PARAM_IS_NULL("10001", "参数不能为空!");
//异常码
private String code;
//异常信息
private String message;
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
三:全局异常捕获类
package cn.ybl.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.ybl.result.JSONResult;
/**
* 全局异常处理
*/
@Component
@Slf4j
@RestControllerAdvice
public class GlobalException {
/**
*捕捉Exception异常
* @return JSONResult
*/
@ExceptionHandler(Exception.class)
public JSONResult exceptionHandler(Exception e){
e.printStackTrace();
log.info("捕捉了Exception异常");
return JSONResult.error("系统错误,请稍后重试","-1");
}
/**
* 捕捉自定义的GlobalCustomException异常
* @param e
* @return JSONResult
*/
@ExceptionHandler(GlobalCustomException.class)
public JSONResult globalCustomException(GlobalCustomException e){
e.printStackTrace();
log.error("发生业务异常,异常码:{},异常信息:{}",e.getErrorCode(),e.getErrorMessage());
return JSONResult.error(e.getErrorMessage(),e.getErrorCode());
}
}
四:JSONResult工具类
package cn.ybl.result;
import cn.ybl.exception.GlobalCustomException;
import lombok.Data;
//返回JSON结果
@Data
//建造者模式
//@Builder
public class JSONResult {
private boolean success = true;
private String message = "成功";
//错误码,用来描述错误类型 ,1000 表示么有错误
private String code = "1000";
//返回的数据
private Object data;
/** 创建当前实例 **/
public static JSONResult success(){
return new JSONResult();
}
/** 创建当前实例 **/
public static JSONResult success(Object obj){
JSONResult instance = new JSONResult();
instance.setData(obj);
return instance;
}
public static JSONResult success(Object obj,String code){
JSONResult instance = new JSONResult();
instance.setCode(code);
instance.setData(obj);
return instance;
}
/** 创建当前实例 **/
public static JSONResult error(String message,String code){
JSONResult instance = new JSONResult();
instance.setMessage(message);
instance.setSuccess(false);
instance.setCode(code);
return instance;
}
public static JSONResult error(GlobalCustomException e){
JSONResult instance = new JSONResult();
instance.setMessage(e.getErrorMessage());
instance.setSuccess(false);
instance.setCode(e.getErrorCode());
return instance;
}
public static JSONResult error(){
JSONResult jsonResult = new JSONResult();
jsonResult.setSuccess(false);
return jsonResult;
}
/** 创建当前实例 **/
public static JSONResult error(String message){
return error(message,null);
}
}
五:断言工具类
package cn.ybl.util;
import cn.ybl.enums.GlobalErrorCode;
import cn.ybl.exception.GlobalCustomException;
import lombok.Data;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 断言工具类
*/
@Data
public class AssertUtil {
private static final String PHONE_CHECK = "^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\\d{8}$";
//手机的正则表达式
private static final Pattern CHINA_PATTERN_PHONE = Pattern.compile(PHONE_CHECK);
/**--------------------------------------------------------
手机号断言
--------------------------------------------------------**/
public static void isPhone(String phone, GlobalErrorCode globalErrorCode){
isNotEmpty(phone, GlobalErrorCode.PARAM_IS_NULL);
Matcher m = CHINA_PATTERN_PHONE.matcher(phone);
if(!m.matches()){
throw new GlobalCustomException(globalErrorCode);
}
}
/**--------------------------------------------------------
断言 不为空,如果为空,抛异常
--------------------------------------------------------**/
public static void isNotEmpty(String text,GlobalErrorCode globalErrorCode) {
if (text == null || text.trim().length() == 0) {
throw new GlobalCustomException(globalErrorCode);
}
}
/**--------------------------------------------------------
断言对象为空
--------------------------------------------------------**/
public static void isNull(Object obj ,GlobalErrorCode globalErrorCode){
if(obj != null){
throw new GlobalCustomException(globalErrorCode);
}
}
public static void isNotNull(Object obj ,GlobalErrorCode globalErrorCode){
if(obj == null){
throw new GlobalCustomException(globalErrorCode);
}
}
/**--------------------------------------------------------
断言false,如果为true,我报错
--------------------------------------------------------**/
public static void isFalse(boolean isFalse ,GlobalErrorCode globalErrorCode){
if(isFalse){
throw new GlobalCustomException(globalErrorCode);
}
}
public static void isTrue(boolean isTrue ,GlobalErrorCode globalErrorCode){
if(!isTrue){
throw new GlobalCustomException(globalErrorCode);
}
}
/**--------------------------------------------------------
断言两个字符串一致
--------------------------------------------------------**/
public static void isEquals(String s1,String s2 ,GlobalErrorCode globalErrorCode){
isNotEmpty(s1, GlobalErrorCode.PARAM_IS_NULL);
isNotEmpty(s2, GlobalErrorCode.PARAM_IS_NULL);
if(!s1.equals(s2)){
throw new GlobalCustomException(globalErrorCode);
}
}
public static void isEqualsTrim(String s1,String s2 ,GlobalErrorCode globalErrorCode){
isNotEmpty(s1, GlobalErrorCode.PARAM_IS_NULL);
isNotEmpty(s2, GlobalErrorCode.PARAM_IS_NULL);
if(!s1.trim().equals(s2.trim())){
throw new GlobalCustomException(globalErrorCode);
}
}
public static void isEqualsIgnoreCase(String s1,String s2 ,GlobalErrorCode globalErrorCode){
isNotEmpty(s1, GlobalErrorCode.PARAM_IS_NULL);
isNotEmpty(s2, GlobalErrorCode.PARAM_IS_NULL);
if(!s1.trim().equalsIgnoreCase(s2.trim())){
throw new GlobalCustomException(globalErrorCode);
}
}
}
六:业务层通过断言工具类进行判断,不符合就抛出异常【isNull,空满足,则不抛异常,不为空则抛异常】
AssertUtil.isNull(user,GlobalErrorCode.USER_EXIST);
======================================自定义全局异常到此结束,也可以不用AssertUtil断言====================================
七:JS303校验
JS303是一套规范,它的存在就是为了优美的参数校验出现的,避免在业务层写大量的数据校验代码
7.1引入依赖【springbootweb包就含有JS303的依赖】
<!--spring-boot-web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
7.2直接在实体类的字段上打上注解【@NotNull、@Email(message="发生错误的提示信息")、@Pattern(regexp="校验规则",message="发生错误的提示信息")】,然后在控制层接收参数的位置打上注解@Valid开启JS303校验支持即可
7.3当JS303校验参数发生异常的时候,就会抛出MethodArgumentNotValidException异常,所以我们需要在全局异常中对这个异常进行捕获,并获取到返回值中我们自己设置的message信息返回给前端
/*
* @Description: 自定义全局参数异常
* @param e: 异常对象
**/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public JsonResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
e.printStackTrace();
// 1.打印日志
log.error("发生方法参数校验异常,异常信息为:" + e.getMessage());
// 2.返回异常信息,异常信息我们需要获取到我们自定义的字段提示语
// 2.1.得到所有的字段异常提示信息
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
StringBuffer sbf = new StringBuffer();
sbf.append("参数校验失败,失败原因为:");
// 2.2.遍历得到所有异常提示语
allErrors.forEach(objectError -> sbf.append(objectError.getDefaultMessage()).append(";"));
return JsonResult.error(sbf.toString());
}