这篇文章主要为大家详细介绍了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;
}
以上就是SpringBoot优雅地实现异常处理的方法详解的详细内容,更多关于SpringBoot全局异常处理的资料请关注编程学习网其它相关文章!
本文标题为:SpringBoot优雅地实现全局异常处理的方法详解
基础教程推荐
- springboot自定义starter方法及注解实例 2023-03-31
- java基础知识之FileInputStream流的使用 2023-08-11
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- Java实现线程插队的示例代码 2022-09-03
- Java文件管理操作的知识点整理 2023-05-19
- java实现多人聊天系统 2023-05-19
- Java数据结构之对象比较详解 2023-03-07
- Java实现查找文件和替换文件内容 2023-04-06
- Java并发编程进阶之线程控制篇 2023-03-07