软件编程
位置:首页>> 软件编程>> java编程>> SpringBoot优雅地实现全局异常处理的方法详解

SpringBoot优雅地实现全局异常处理的方法详解

作者:慕歌  发布时间:2023-09-06 09:17:02 

标签:SpringBoot,全局,异常,处理

前言

在前一节的学习中,慕歌带大家使用了全局结果集返回,通过使用全局结果集配置,优雅的返回后端数据,为前端的数据拿取提供了非常好的参考。同时通过不同的状态码返回,我们能够清晰的了解报错的位置,排除错误。如果大家有需要,可以使用我提供的的同一结果集以及状态码,并且可以使用全局异常拦截,实现异常的标准返回。接下来,我们一起来了解如何使用全局异常处理吧!

异常工具

先定义一个合适 的异常处理类,在之后的异常都会以这种格式返回前端,前端根据我们的异常进行自己的返回,以一种优雅的方式呈现错误,优化用户体验。
异常结果集:

/**
* 返回结果封装
*/

@Data
public class ResultVo {
   // 状态码
   private int code;

// 状态信息
   private String msg;

// 返回对象
   private Object data;

// 手动设置返回vo
   public ResultVo(int code, String msg) {
       this.code = code;
       this.msg = msg;
   }

// 手动设置返回vo
   public ResultVo(int code, String msg, Object data) {
       this.code = code;
       this.msg = msg;
       this.data = data;
   }

// 只返回状态码
   public ResultVo(StatusCode statusCode) {
       this.code = statusCode.getCode();
       this.msg = statusCode.getMsg();
   }

// 默认返回成功状态码,数据对象
   public ResultVo(Object data) {
       this.code = ResultCode.SUCCESS.getCode();
       this.msg = ResultCode.SUCCESS.getMsg();
       this.data = data;
   }

// 返回指定状态码,数据对象
   public ResultVo(StatusCode statusCode, Object data) {
       this.code = statusCode.getCode();
       this.msg = statusCode.getMsg();
       this.data = data;
   }

public ResultVo(StatusCode statusCode,String msg, Object data) {
       this.code = statusCode.getCode();
       this.msg = msg;
       this.data = data;
   }
}

异常状态码,通过返回的状态码,以及状态信息,能够高效反映错误,并且可以全局统一管理,方便快捷:

@Getter
public enum ExceptionCode implements StatusCode {

// 系统级别错误码
   ERROR(-1, "操作异常"),
   NOT_LOGIN(102, "请先登录!"),
   NO_Role(102,"无权限"),
   NO_PERMISSION(102,"无权限"),
   OUT_TIME(102,"登录信息过期"),
   DISABLE_ACCOUNT(102,"帐号已被禁用!"),
   EMAIL_DISABLE_LOGIN(102,"该邮箱账号已被管理员禁止登录!"),
   IP_REPEAT_SUBMIT(102,"访问次数过多,请稍后重试"),
   ERROR_DEFAULT(105,"系统繁忙,请稍后重试");

//异常码
   private int code;
   //异常信息
   private String msg;

//自定义方法
   ExceptionCode(int code, String msg) {
       this.code = code;
       this.msg = msg;
   }
}

当我们对异常通过以上工具类进行封装之后,所有异常将以一种固定的格式返回,不会导致错乱:

{3 items
"code":105
"msg":"系统繁忙,请稍后重试"
"data":NULL
}

异常处理

在spring boot中需要使用异常 * ,拦截全局的异常,不直接将异常返回,而是在我们进行处理之后,以一种清晰可读的方式返回。并且前端能够清晰解读我们的异常,呈现给用户。

//捕获校验器异常
@RestControllerAdvice
public class ControllerExceptionAdvice {
   @ExceptionHandler({BindException.class})
   public ResultVo ValidExceptionHandler(BindException e) {
       // 从异常对象中拿到ObjectError对象
       ObjectError objectError = e.getBindingResult().getAllErrors().get(0);

return new ResultVo(ResultCode.VALIDATE_ERROR.getCode(),objectError.getDefaultMessage());
   }
}

对特定异常进行拦截,并包装异常:

/**
* 对返回结果进行包装
*/
@RestControllerAdvice(basePackages = {"channel.cert"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
   @Override
   public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
       // response是ResultVo类型,或者注释了NotControllerResponseAdvice都不进行包装
       return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);
   }

@Override
   public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
       // String类型不能直接包装
       if (returnType.getGenericParameterType().equals(String.class)) {
           ObjectMapper objectMapper = new ObjectMapper();
           try {
               // 将数据包装在ResultVo里后转换为json串进行返回
               return objectMapper.writeValueAsString(new ResultVo(data));
           } catch (JsonProcessingException e) {
               throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());
           }
       }
       // 否则直接包装成ResultVo返回
       return new ResultVo(data);
   }
}

异常捕捉

自定义异常:

@Getter
public class APIException extends RuntimeException {
   private int code;
   private String msg;

//自定义异枚举
   public APIException(StatusCode statusCode){
       super(statusCode.getMsg());
       this.code = statusCode.getCode();
       this.msg = statusCode.getMsg();
   }

// 手动设置异常
   public APIException(StatusCode statusCode, String message) {
       // message用于用户设置抛出错误详情,例如:当前价格-5,小于0
       super(message);
       // 状态码
       this.code = statusCode.getCode();
       // 状态码配套的msg
       this.msg = statusCode.getMsg();
   }

// 默认异常使用APP_ERROR状态码
   public APIException(String errorMsg) {
       super(errorMsg);
       this.code = ExceptionCode.ERROR_DEFAULT.getCode();
       this.msg = ExceptionCode.ERROR_DEFAULT.getMsg();
   }

//自定义参数 错误码 错误信息
   public APIException(int errorCode, String errorMsg) {
       super(errorMsg);
       this.code = errorCode;
       this.msg = ExceptionCode.ERROR_DEFAULT.getMsg();
   }

//自定义参数 错误码 错误信息 异常
   public APIException(int errorCode, String errorMsg, Throwable cause) {
       super(errorMsg);
       this.code = errorCode;
       this.msg = errorMsg;
   }
}

对自定义异常进行捕获,通过定义好的异常的结果集返回。

/**
* 全局异常处理
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

// Assert业务异常
   @ExceptionHandler(IllegalArgumentException.class)
   public ResultVo AssertExceptionHandler(IllegalArgumentException ex) {
       log.error( " msg : " + ex.getMessage(), ex);
       if(StringUtils.isBlank(ex.getLocalizedMessage())){
           return new ResultVo(ExceptionCode.ERROR_DEFAULT);
       }
       return new ResultVo(ex.getMessage());
   }

// 登录失效异常
   @ExceptionHandler(SaTokenException.class)
   public ResultVo LoginOutExceptionHandler(SaTokenException ex) {
       log.error( " msg : " + ex.getMessage(), ex);
       return new ResultVo(ExceptionCode.OUT_TIME);
   }

// 登录异常
   @ExceptionHandler(NotLoginException.class)
   public ResultVo NotLoginExceptionHandler(NotLoginException ex) {
       log.error( " msg : " + ex.getMessage(), ex);
       return new ResultVo(ExceptionCode.NOT_LOGIN);
   }

// 权限异常
   @ExceptionHandler(NotPermissionException.class)
   public ResultVo NotPermissionExceptionHandler(NotPermissionException ex) {
       log.error( " msg : " + ex.getMessage(), ex);
       return new ResultVo(ExceptionCode.NO_PERMISSION);
   }

//角色异常
   @ExceptionHandler(NotRoleException.class)
   public ResultVo NotRoleExceptionHandler(NotRoleException ex) {
       log.error( " msg : " + ex.getMessage(), ex);
       return new ResultVo(ExceptionCode.NO_Role);
   }

//处理自定义异常
   @ExceptionHandler(APIException.class)
   public ResultVo APIExceptionHandler(APIException e) {
       log.error(e.getMessage(), e);
       return new ResultVo(e.getCode(), e.getMsg());
   }

//处理运行异常
   @ExceptionHandler(RuntimeException.class)
   public ResultVo RuntimeExceptionHandler(RuntimeException e) {
       log.error(e.getMessage(), e);
       return new ResultVo(ExceptionCode.ERROR_DEFAULT);
   }
}

通过以上自定义,我们就能在项目中,使用自定义异常,在我们认为可能的报错处插入,当发生错误时,我们更快定位是之前记录的错误点导致。

//查询数字证书
   @Override
   public GroupUser searchCert(String certCode) {
       try {
           //查询证书编号
           GroupUser user = queryCert(certCode);
           if(ObjectUtil.isNotNull(user)){
               return user;
           }
       }catch (Exception e){
           throw new APIException("区块链调用失败"+e);
       }
       return null;
   }

来源:https://juejin.cn/post/7132836592368959496

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com