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全局异常处理的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot全局异常处理方案分享

    目录 一 业务场景 二 全局系统异常类 一)全局系统异常类 二) 包装异常返回结果给前端,修改自定义异常 三 返回案例 一 业务场景 调用接口时需要对属性进行校验,比如属性长度,当属性为邮箱时校验邮箱格式等,这时候要要用到@Validated注解,在使用这个注解后发现出现了一个问题,调用接口后并没有返回我们想要的报错结果,而是返回了 "message": "Validation failed for object='IMMessageSend'. Error count: 1

  • SpringBoot全局异常处理方式

    目录 SpringBoot全局异常处理 springboot全局异常处理--@ControllerAdvice+ExceptionHandler 一.全局捕获异常后,返回json给浏览器 二.全局捕获异常后,返回页面给浏览器 SpringBoot全局异常处理 为了让客户端能有一个更好的体验,当客户端发送请求到服务端发生错误时服务端应该明确告诉客户端错误信息. SpringBoot内置的异常处理返回的界面太杂乱,不够友好.我们需要将异常信息做封装处理响应给前端.本文介绍的为将错误信息统一封装成如下

  • SpringBoot接口如何统一异常处理

    目录 为什么要优雅的处理异常 实现案例 @ControllerAdvice异常统一处理 Controller接口 运行测试 进一步理解 @ControllerAdvice还可以怎么用? @ControllerAdvice是如何起作用的(原理)? 为什么要优雅的处理异常 如果我们不统一的处理异常,经常会在controller层有大量的异常处理的代码, 比如: @Slf4j @Api(value = "User Interfaces", tags = "User Interfac

  • SpringBoot实现全局异常处理方法总结

    目录 全局异常处理 配置全局异常 全局异常处理的升级 加入自定义异常处理 处理Controller数据绑定.数据校验的异常 GlobalExceptionHandler全部代码 总结 在项目开发中出现异常时很平常不过的事情,我们处理异常也有很多种方式,可能如下: public int div(int a ,int b){ int c=0; try{ c=a/b; }catch (Exception ex){ ex.printStackTrace(); } return c; } 如果我们这样处理

  • SpringBoot中异常处理实战记录

    目录 一.背景 二.需求 三.编写一些异常基础代码 四.注意事项 五.总结 六.代码实现 七.参考文档 一.背景 在我们编写程序的过程中,程序中可能随时发生各种异常,那么我们如何优雅的处理各种异常呢? 二.需求 1.拦截系统中部分异常,返回自定义的响应. 比如: 系统发生HttpRequestMethodNotSupportedException异常,我们需要返回如下信息. http的状态码:返回 405 { code: 自定义异常码, message: 错误消息 } 2.实现自定义异常的拦截

  • Springboot项目异常处理及返回结果统一

    目录 背景 返回结果定义 异常的定义 异常的处理 返回结果的处理 完整代码 使用示例 背景 在创建项目的初期,我们需要规范后端返回的数据结构,以便更好地与前端开发人员合作. 比如后端返回的数据为: {  "msg": "请跳转登陆页面", } 此时前端无法确定后端服务的处理结果是成功的还是失败的.在前端展示页面,成功与失败的展示是要作区分的,甚至不同的成功或失败结果要做出不同的展现效果,这也就是我们为什么要对返回结果做出统一规范的原因. 返回结果定义 public

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

    目录 前言 异常工具 异常处理 异常捕捉 前言 在前一节的学习中,慕歌带大家使用了全局结果集返回,通过使用全局结果集配置,优雅的返回后端数据,为前端的数据拿取提供了非常好的参考.同时通过不同的状态码返回,我们能够清晰的了解报错的位置,排除错误.如果大家有需要,可以使用我提供的的同一结果集以及状态码,并且可以使用全局异常拦截,实现异常的标准返回.接下来,我们一起来了解如何使用全局异常处理吧! 异常工具 先定义一个合适 的异常处理类,在之后的异常都会以这种格式返回前端,前端根据我们的异常进行自己的返

  • Spring Cloud Gateway全局异常处理的方法详解

    前言 Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式.Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等

  • Springboot配置返回日期格式化五种方法详解

    目录 格式化全局时间字段 1.前端时间格式化(不做无情人) 2.SimpleDateFormat格式化(不推荐) 3.DateTimeFormatter格式化(不推荐) 4.全局时间格式化(推荐) 实现原理分析 5.部分时间格式化(推荐) 总结 应急就这样 格式化全局时间字段 在yml中添加如下配置: spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 或者 spring: jackson: ## 格式为yyyy-MM-dd HH:mm:ss date-

  • SpringBoot+Vue实现EasyPOI导入导出的方法详解

    目录 前言 一.为什么做导入导出 二.什么是 EasyPOI 三.项目简介 项目需求 效果图 开发环境 四.实战开发 核心源码 前端页面 后端核心实现 五.项目源码 小结 前言 Hello~ ,前后端分离系列和大家见面了,秉着能够学到知识,学会知识,学懂知识的理念去学习,深入理解技术! 项目开发过程中,很大的需求都有 导入导出功能,我们依照此功能,来实现并还原真实企业开发中的实现思路 一.为什么做导入导出 为什么做导入导出 导入 在项目开发过程中,总会有一些统一的操作,例如插入数据,系统支持单个

  • SpringBoot使用AOP统一日志管理的方法详解

    目录 前言 实现 1.引入依赖 2.定义logback配置 3.编写切面类 4.测试 前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国外知名开源框架JHipster的AOP日志管理方式 1.引入依赖 <!-- spring aop --> <dependency> <groupId>org.springframework

  • SpringBoot集成JWT实现登陆验证的方法详解

    1:首先,我们需要在项目中导入两个依赖: <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifa

  • SpringBoot整合EasyExcel进行大数据处理的方法详解

    目录 EasyExcel 需要的Maven 基础读案例 操作的excel 实体类 读取监听器 测试 基础写案例 实体类 测试 Excel模板方式 准备模块 实体类 测试 EasyExcel EasyExcel文档 我用过Poi和EasyPoi这些工具总体来说: POI 优点我觉得自由,但是迎来的就是复杂度,和大数据量时候性能的缺点 EasyPoi基于POI 的二次封装,解决了大部分的常用场景,简化了代码,但是特别复杂表格处理还是不行,而且性能的话和poi差不多,简单来说就是简化了Poi的操作,少

  • SpringBoot处理接口幂等性的两种方法详解

    目录 1. 接口幂等性实现方案梳理 1.1 基于 Token 1.2 基于请求参数校验 2. 基于请求参数的校验 在上周发布的 TienChin 项目视频中,我和大家一共梳理了六种幂等性解决方案,接口幂等性处理算是一个非常常见的需求了,我们在很多项目中其实都会遇到.今天我们来看看两种比较简单的实现思路. 1. 接口幂等性实现方案梳理 其实接口幂等性的实现方案还是蛮多的,我这里和小伙伴们分享两种比较常见的方案. 1.1 基于 Token 基于 Token 这种方案的实现思路很简单,整个流程分两步:

  • SpringBoot使用thymeleaf实现一个前端表格方法详解

    目录 1. User 实体类 2. Controller 类 3. html 文件 1. User 实体类 注:这里使用了 Lombok 技术,通过 @Data 注释自动创建 get,set 方法:通过 @NoArgsConstructor 注释自动创建无参数的构造方法:通过 @AllArgsConstructor 注释自动创建有参数构造方法 如果不想使用,可以自行创建get,set 方法以及构造方法 import jdk.nashorn.internal.objects.annotations

  • 更优雅的C++字符串格式化实现方法详解

    目录 背景 优化 实现 使用 背景 在用C++编写代码时,经常需要用到字符串拼接及格式化,尤其是在拼写sql语句时,目前大部分sql拼接方式都是通过ostringstream流一点一点拼接的,代码可读性很差而且很容易拼接错误 ostringstream sqlstr; sqlstr << "insert into virtual_item_info(id, platform, typeid, name, icon_url, act_url, " "desc_tex

随机推荐