SpringBoot优雅地实现全局异常处理的方法详解
作者:慕歌 时间:2023-09-06 09:17:02
前言
在前一节的学习中,慕歌带大家使用了全局结果集返回,通过使用全局结果集配置,优雅的返回后端数据,为前端的数据拿取提供了非常好的参考。同时通过不同的状态码返回,我们能够清晰的了解报错的位置,排除错误。如果大家有需要,可以使用我提供的的同一结果集以及状态码,并且可以使用全局异常拦截,实现异常的标准返回。接下来,我们一起来了解如何使用全局异常处理吧!
异常工具
先定义一个合适 的异常处理类,在之后的异常都会以这种格式返回前端,前端根据我们的异常进行自己的返回,以一种优雅的方式呈现错误,优化用户体验。
异常结果集:
/**
* 返回结果封装
*/
@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
标签:SpringBoot,全局,异常,处理
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
C#接口在派生类和外部类中的调用方法示例
2021-06-13 07:54:43
java base64编码、解码的三种方式总结
2022-12-29 22:20:51
Android使用系统相机进行拍照的步骤
2023-10-07 16:05:05
SpringMVC @NotNull校验不生效的解决方案
2021-07-19 23:20:07
关于多线程常用方法以及对锁的控制(详解)
2022-02-02 08:16:42
WPF基于物理像素绘制图形
2022-01-06 20:25:18
![](https://img.aspxhome.com/file/2023/3/98243_0s.png)
Java擦除和转换实例分析
2022-03-30 02:19:45
IDEA配置maven环境的详细教程(Unable to import maven project报错问题的解决)
2022-04-09 13:53:40
![](https://img.aspxhome.com/file/2023/0/71000_0s.jpg)
c#求范围内素数的示例分享(c#求素数)
2022-07-09 22:20:10
Spring5中的WebClient使用方法详解
2023-08-05 14:50:24
Java之int和string类型转换详解
2023-11-06 04:10:06
C#影院售票系统毕业设计(3)
2023-04-24 04:08:57
Java ConcurrentHashMap的使用示例
2023-02-04 09:04:29
java常量字符串过长报错的解决办法以及原因分析
2023-10-18 10:15:25
![](https://img.aspxhome.com/file/2023/2/80922_0s.png)
使用Springboot 打jar包实现分离依赖lib和配置
2023-01-29 18:35:29
![](https://img.aspxhome.com/file/2023/7/76537_0s.png)
Mybatis 逆向工程的三种方法详解
2023-08-10 22:27:20
![](https://img.aspxhome.com/file/2023/5/88585_0s.png)
Android仿美团淘宝实现多级下拉列表菜单功能
2022-07-24 18:42:18
![](https://img.aspxhome.com/file/2023/4/137114_0s.png)
Java动态代理之拦截器的应用
2022-11-24 11:18:55
Java基础知识之CharArrayReader流的使用
2023-02-12 10:40:29
![](https://img.aspxhome.com/file/2023/0/61010_0s.png)
SpringBoot 实现动态添加定时任务功能
2022-07-12 05:07:40
![](https://img.aspxhome.com/file/2023/6/102646_0s.png)