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
投稿
猜你喜欢
- 1、什么是Mybatis?MyBatis是一个优秀的持久层框架,是一个半ORM(对象关系映射)框架,它对jdbc的操作数据库的过程进行封装,
- 在类中自定义的“函数”称为“方法”,由于C#是完全面向对象的
- 一、Shiro简介:Apache Shiro是一个Java的安全(权限)框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在J
- 在Java中从字符串中删除空格有很多不同的方法,如trim,replaceAll等。但是,在JDK 11添加了一些新的功能,如strip、s
- Lucene从今天开始,我们要开始介绍Lucene中索引构建的流程。因为索引构建的逻辑涉及到的东西非常多,如果从构建入口IndexWrite
- 需求基于MTK8163 8.1平台定制导航栏部分,在左边增加音量减,右边增加音量加思路需求开始做之前,一定要研读SystemUI Navig
- 咱们废话不多说进入主题、系统主页展示:用户登录后进行系统首页:主要功能模块如下、分角色管理、超级管理员拥有最高权限、可以进行菜单灵活控制、用
- 1. 添加maven依赖包<dependency> <groupId>org.apache.calcit
- 一般情况下是不可以用static修饰类的。如果一定要用static修饰类的话,通常static修饰的是匿名内部类。在一个类中创建另外一个类,
- 一、前言文稿扫描大家用的都比较频繁、想是各种证件、文件都可以通过扫描文稿功能保存到手机。相比直接拍照,在扫描文稿时,程序会对图像进行一些矫正
- 这篇文章主要介绍了如何基于Java实现对象List排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 一、项目背景1、介绍:最近在springboot项目中需要做一个阿里云OSS图片上传功能点,将OSS图片上传代码提取到公共工具类中,为了方便
- 前言:发现用Winform做一个圆角按钮遇到麻烦,主要是锯齿问题,后面想了想办法解决问题了。主要方法是按钮的区域通过Region指定,但按钮
- Java类加载器1、BootClassLoader: 用于加载Android Framework层class文件。2、PathClassLo
- Mybatis入门-基于配置实现单表的增删改查Mybatis简介官网链接:https://mybatis.org/mybatis-3/zh/
- Spring底层核心原理下面这几行代码是一个Spring的入门代码,第一行是通过java配置类 注解的方式创建一个Spring容器,第二行是
- Handler每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部
- Spring-boot JMS 发送消息慢的问题解决1、在《ActiveMQ 基于zookeeper的主从(levelDB Master/S
- 本文实例讲述了C#实现类似新浪微博长URL转短地址的方法。分享给大家供大家参考。具体如下:一、前台判断用户输入URL的JS代码如下。func
- 需求: 使用IO流将指定目录下的若干个音频文件的高潮部分,进行剪切,并重新拼接成一首新的音频文件 思路(以两首歌为例):第一首歌有