Spring Boot进阶(50):Spring Boot如何全局统一处理异常?| 超级详细,建议收藏

news2024/12/25 9:34:13

1. 前言🔥

        今天和大家讨论的是Spring Boot如何统一处理异常。这里先说一下我们为什么需要全局统一处理异常?其实理由很简单,因为程序在运行的过程中,不可避免会产生各种各样的错误。比如说用户传过来的参数不正确,无法连接上数据库,或者在计算某个任务的时候超时等。所以我们一般需要合理的抛出各种异常信息。这些异常信息,一旦不处理,前端就会得到一个500的服务器内部错误,直接展示非常的不友好不优雅,所以我们需要将这些异常捕获,并告知前端,到底错在了哪里。另一个问题是,我们不可能直接将完整的异常信息返回,因为可能涉及到一些内部的重要信息,不能随意泄露,所以我们还需要对异常信息进行过滤和转换,值给前端返回可读的,简洁的,准确的说明信息。

这就是为什么我们需要全局异常处理。

        这将又会是干货满满的一期,全程无尿点不废话只抓重点教,具有非常好的学习效果,拿好小板凳准备就坐!希望学习的过程中大家认真听好好学,学习的途中有任何不清楚或疑问的地方皆可评论区留言或私信,bug菌将第一时间给予解惑,那么废话不多说,直接开整!Fighting!! 

2. 环境说明🔥

本地的开发环境:

  • 开发工具:IDEA 2021.3
  • JDK版本: JDK 1.8
  • Spring Boot版本:2.3.1 RELEASE
  • Maven版本:3.8.2

3. 正文🔥 

        这里bug菌提供的一种思路是使用全局异常处理器。全局异常处理器不仅能够捕获默认的异常,还能够捕获各种自定义异常。一个简单的全局异常处理器代码如下:

3.1 定义全局异常处理器

GlobalExceptionHandler.java

package com.example.demo.exception;

import com.example.demo.enums.ErrorCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author luoYong
 * @version 1.0
 * @date 2023/6/13 11:24
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private Map<String, Object> getResult(ErrorCodeEnum e, boolean status) {
        Map<String, Object> map = new HashMap<>();
        map.put("success", status);
        map.put("code", e.getKey());
        map.put("msg", e.getValue());
        return map;
    }

    /**
     * 参数校验不通过
     *
     * @param e       异常信息
     * @param request 请求信息
     */
    @ExceptionHandler(value = ParamsException.class)
    @ResponseStatus(HttpStatus.OK)
    public Map<String, Object> handleObjectExistException(ParamsException e,
                                                          HttpServletRequest request) {
        return getResult(ErrorCodeEnum.PARAM_EXIST_EXCEPTION, false);
    }

    /**
     * 用户校验不通过
     *
     * @param e       异常信息
     * @param request 请求信息
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = TokenExpireException.class)
    public Map<String, Object> handle(TokenExpireException e, HttpServletRequest request) {
        return getResult(ErrorCodeEnum.USER_NOT_LOGIN, false);
    }

    /**
     * 全局异常处理
     *
     * @param e       异常信息
     * @param request 请求信息
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = SystemRunningException.class)
    public Map<String, Object> handle(SystemRunningException e, HttpServletRequest request) {
        return getResult(ErrorCodeEnum.SYSTEM_RUNNING, false);
    }

    /**
     * 全局异常处理
     *
     * @param e       异常信息
     * @param request 请求信息
     */
    @ResponseStatus()
    @ExceptionHandler(value = Throwable.class)
    public Map<String, Object> handle(Exception e, HttpServletRequest request) {
        return getResult(ErrorCodeEnum.SYSTEM_ERROR, false);
    }


}

        这里我对所有的异常信息返回结果都做了统一的处理,只返回success,code,msg三个字段。在内部封装了一个getResult私有方法,便于统一处理,然后分别使用不同的方法处理不同的异常信息,比如ParamsException,TokenExpireException,SystemRunningException等

以上的这仨皆属于自定义异常,代码如下:

3.2 定义参数异常ParamsException类

        这里先定义参数异常的信息扑捉,思路也比较简单,就是除了msg之外,不需要接收code,只便于更加灵活的实现返回值数据。

package com.example.demo.exception;

import com.example.demo.enums.ErrorCodeEnum;

/**
 * 参数异常
 *
 * @Author luoYong
 * @Date 2022-03-30 13:00
 */
public class ParamsException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private String message;

    public ParamsException() {
    }

    public ParamsException(String msg) {
        this.message = msg;
    }

    public ParamsException(ErrorCodeEnum e) {
        this.message = e.getValue();
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

3.3 定义异常TokenExpireException类

        这里我们再来定义一个扑捉token用户登陆信息过期的异常类,目的是更直接用于相关token异常信息的捕捉。由于我们直接也是定义了默认的token Error错误码枚举,顾我们也是只需要定义msg信息的接收即可,但如果你都想自定义,那你就把对应的code、success、msg都定义赋值也行,具体代码如下:

package com.example.demo.exception;

import com.example.demo.enums.ErrorCodeEnum;

/**
 * 用户登陆信息过期异常
 */
public class TokenExpireException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private String message;

    public TokenExpireException() {
    }

    public TokenExpireException(String msg) {
        this.message = msg;
    }

    public TokenExpireException(ErrorCodeEnum e) {
        this.message = e.getValue();
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

3.4 定义一个ExceptionController测试

        这里我们写两个接口直接模拟异常返回,查阅具体的异常信息是否被正常捕捉,然后其他的测试你们就自己玩啦,这里给大家举个例子。

3.4.1 测试参数异常

        我们直接定义一个Get请求,然后设置一个参数,我们在传值的过程中,我们直接传空,我们即可验证是否能捕获参数异常的全局信息返回。

@RestController
@RequestMapping("/exception")
@Api(tags = "全局异常测试模块", description = "全局异常测试模块")
public class ExceptionController {

    @Autowired
    private UserService userService;

    /**
     * 根据用户id查询用户信息
     */
    @GetMapping("/find-user-by-id")
    @ApiOperation(value = "根据用户id查询用户信息", notes = "根据用户id查询用户信息")
    public ResultResponse<UserEntity> saveUser(@RequestParam("userId") String userId) throws ParamsException {
        if (StringUtils.isBlank(userId)) {
            throw new ParamsException(PARAM_EXIST_EXCEPTION);
        }
        return new ResultResponse<>(userService.getById(userId));
    }
}

3.4.2 Swagger请求校验

        重启项目,我们将userId参数传空格,我们直接请求,可以看看接口返回体具体是啥内容?

        可以看到,正是我们展示的PARAM_EXIST_EXCEPTION(101001, "参数异常")这句,我们也可以自定义,也可以采用默认的提示,成功的并统一处理了返回结果。

3.4.3 测试token异常

        测试代码如下,仅供参考:

    @GetMapping("/login")
    @ApiOperation(value = "登录", notes = "登录")
    public ResultResponse<Boolean> login(@RequestParam("token") String token) throws TokenExpireException {
        throw new TokenExpireException();
    }

3.4.4 Swagger请求校验

        重启项目后请求一遍,可以看下接口的返回值如下:

         可以发现,我们成功的捕获到了默认的Exception异常和自定义的TokenExpireException

异常,并统一处理了返回结果。

        两轮测试结果比较令人满意,希望能帮助到大家,剩下的测试或者自定义异常,你们可以大胆发挥,这里我就不一一赘述啦。

3.5 附上接口返回错误码枚举类

        具体代码如下,仅供参考:

/**
 * 接口返回错误码枚举
 */
public enum ErrorCodeEnum implements IEnum {

    /* 系统异常 */
    SYSTEM_RUNNING(100000, "系统运行异常"),
    SYSTEM_ERROR(101000, "系统未知异常"),
    PARAM_EXIST_EXCEPTION(101001, "参数异常"),

    /* token相关 */
    TOKEN_IS_EMPTY(102001, "该请求没有携带token!请先获取token"),
    TOKEN_IS_INVALID(102002, "token失效,请重新登录!"),
    TOKEN_IS_ERROR(102003, "非法token!请重新登录!"),
    USER_NOT_LOGIN(103006, "请先登录"),


    ;
    private Integer key;
    private String value;

    ErrorCodeEnum(Integer key, String value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public Integer getKey() {
        return this.key;
    }

    @Override
    public String getValue() {
        return this.value;
    }

}

4. 热文推荐🔥

滴~如下推荐【Spring Boot 进阶篇】的学习大纲,请小伙伴们注意查收。

Spring Boot进阶(01):Spring Boot 集成 Redis,实现缓存自由

Spring Boot进阶(02):使用Validation进行参数校验

Spring Boot进阶(03):如何使用MyBatis-Plus实现字段的自动填充

Spring Boot进阶(04):如何使用MyBatis-Plus快速实现自定义sql分页

Spring Boot进阶(05):Spring Boot 整合RabbitMq,实现消息队列服务

Spring Boot进阶(06):Windows10系统搭建 RabbitMq Server 服务端

Spring Boot进阶(07):集成EasyPoi,实现Excel/Word的导入导出

Spring Boot进阶(08):集成EasyPoi,实现Excel/Word携带图片导出

Spring Boot进阶(09):集成EasyPoi,实现Excel文件多sheet导入导出

Spring Boot进阶(10):集成EasyPoi,实现Excel模板导出成PDF文件

Spring Boot进阶(11):Spring Boot 如何实现纯文本转成.csv格式文件?

Spring Boot进阶(12):Spring Boot 如何获取Excel sheet页的数量?

Spring Boot进阶(13):Spring Boot 如何获取@ApiModelProperty(value = “序列号“, name = “uuid“)中的value值name值?

Spring Boot进阶(14):Spring Boot 如何手动连接库并获取指定表结构?一文教会你

Spring Boot进阶(15):根据数据库连接信息指定分页查询表结构信息

Spring Boot进阶(16):Spring Boot 如何通过Redis实现手机号验证码功能?

Spring Boot进阶(17):Spring Boot如何在swagger2中配置header请求头等参数信息

Spring Boot进阶(18):SpringBoot如何使用@Scheduled创建定时任务?

Spring Boot进阶(19):Spring Boot 整合ElasticSearch

Spring Boot进阶(20):配置Jetty容器

Spring Boot进阶(21):配置Undertow容器

Spring Boot进阶(22):Tomcat与Undertow容器性能对比分析

Spring Boot进阶(23):实现文件上传

Spring Boot进阶(24):如何快速实现多文件上传?

Spring Boot进阶(25):文件上传的单元测试怎么写?

Spring Boot进阶(26):Mybatis 中 resultType、resultMap详解及实战教学

Spring Boot进阶(27):Spring Boot 整合 kafka(环境搭建+演示)

Spring Boot进阶(28):Jar包Linux后台启动部署及滚动日志查看,日志输出至实体文件保存

Spring Boot进阶(29):如何正确使用@PathVariable,@RequestParam、@RequestBody等注解?不会我教你,结合Postman演示

Spring Boot进阶(30):@RestController和@Controller 注解使用区别,实战演示

...

5. 文末🔥

        如果想系统性的学习Spring Boot,小伙伴们直接订阅bug菌专门为大家创建的Spring Boot专栏《滚雪球学Spring Boot》从入门到精通,从无到有,从零到一!以知识点+实例+项目的学习模式由浅入深对Spring Boot框架进行学习&使用。

       我是bug菌,一名想走👣出大山改变命运的程序猿。接下来的路还很长,都等待着我们去突破、去挑战。来吧,小伙伴们,我们一起加油!未来皆可期,fighting!

关注公众号,获取最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等硬核资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/649225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4端到端协议-4.2【实验】【计算机网络】

4端到端协议-4.2【实验】【计算机网络】 前言推荐4端到端协议4.2 TCP协议流捕获与TCP协议分析实验目的实验内容及实验环境实验原理实验过程实验过程演示 4.2.1实验章节测验一.单选题&#xff08;共5题,25.0分&#xff09;二.阅读理解&#xff08;共1题,70.0分&#xff09;三.填…

xxl-job核心源码解析

xxl-job源码解析 如何自研一个xxljob 注册服务调度服务RPC组件(基建&#xff0c;底层严重依赖)日志服务告警服务 系统架构 执行流程 各大调度中心对比 1&#xff09;服务端启动流程 首先找到配置类 XxlJobAdminConfig 可以发现该类实现 InitializingBean接口&#xff0c;…

openpose保姆级教程代码详细解析——训练部分

一&#xff1a;前言 OpenPose是一个基于深度学习的人体姿势估计库&#xff0c;它可以从图像或视频中准确地检测和估计人体的关键点和姿势信息。OpenPose的目标是将人体姿势估计变成一个实时、多人、准确的任务。它的原理部分已经在上一篇非常详细的讲解了——本节介绍训…

Dynamic .NET TWAIN 8.3.3 for NetCore Crack

用于 WinForms 和 WPF 应用程序的扫描仪和网络摄像头捕获 SDK 适用于 .NET 应用程序的简单高效的扫描和网络摄像头 SDK Dynamsoft 的 Dynamic .NET TWAIN 是一个基于 TWAIN 和 DirectShow 标准的 .NET 文档成像 SDK。它为扫描仪和网络摄像头软件开发提供了丰富、高效且…

修改依赖包下的子依赖版本,前端项目安全扫描出来的漏洞——解决过程

为什么要升级&#xff0c;如图云桌面&#xff08;相当于堡垒机-远程桌面&#xff09;的项目审查是大概基于node16版本进行扫描的&#xff0c;本来我方是通过降版本从14到12绕过大范围更新&#xff0c;但现在躲得过初一躲不过十五&#xff0c;如何更新 package-lock.json 中的一…

DAY24:二叉树(十四)二叉搜索树中的插入操作+删除二叉搜索树中的节点(二叉树结构修改难点)

文章目录 701.二叉搜索树中的插入操作思路递归法如何保证连接的节点就是空节点的父节点&#xff1f; 迭代法迭代法注意debug测试 450.删除二叉搜索树中的节点&#xff08;坑较多&#xff0c;注意复盘&#xff09;思路最开始的写法debug测试1.使用了释放后的空间ERROR: AddressS…

通知!2023年湖北住建厅七大员新考和继续教育要求有变化了?启程别

通知&#xff01;2023年湖北住建厅七大员新考和继续教育要求有变化了&#xff1f;启程别 湖北住建厅七大员新考以及继续教育的相关要求都即将有一些变化了目前在征集意见的阶段&#xff0c;具体实施等后续具体通知 对于新考的变化主要是&#xff1a; 1.由原先报名之后只需要完成…

Vue中如何进行颜色选择与调色板

Vue中如何进行颜色选择与调色板 颜色选择和调色板是Web开发中常用的功能&#xff0c;它们可以帮助用户选择或调整颜色。Vue作为一个流行的JavaScript框架&#xff0c;提供了一些工具和库&#xff0c;可以方便地实现颜色选择和调色板功能。本文将介绍如何在Vue中进行颜色选择和…

【aspose-words】Aspose.Words for Java模板语法详细剖析

文章目录 前言&#x1f34a;缘由aspose-words模板语法再了解 &#x1f3af;主要目标实现3大重点 &#x1f381;快速链接&#x1f348;猜你想问如何与狗哥联系进行探讨1.关注公众号【JavaDog程序狗】2.踩踩狗哥博客 &#x1f36f;猜你喜欢文章推荐 正文&#x1f34b;aspose-word…

mfc140.dll丢失的解决方法,解析mfc140.dll这个文件

其实大部分人在使用计算机过程中&#xff0c;您可能会遇到mfc140.dll丢失的错误提示。这个错误会导致一些应用程序无法正常运行&#xff0c;那么要如何解决这个问题呢&#xff1f;今天小编就来给大家详细的解析mfc140.dll这个文件以及教大家 mfc140.dll丢失的解决方法。 目录 …

【算法与数据结构】349、LeetCode两个数组的交集

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;使用了一个哈希数组记录nums1出现的数字&#xff0c;然后遍历nums2&#xff0c;找到hash数组为1的值就…

Vue-Element-Admin项目学习笔记(6)Vuex状态管理

前情回顾&#xff1a; vue-element-admin项目学习笔记&#xff08;1&#xff09;安装、配置、启动项目 vue-element-admin项目学习笔记&#xff08;2&#xff09;main.js 文件分析 vue-element-admin项目学习笔记&#xff08;3&#xff09;路由分析一:静态路由 vue-element-adm…

yoloV5目标识别以及跟踪,功能识别动物(大象,犀牛,水牛,斑马)

yolo目标识别以及跟踪还是很强的嘞&#xff01; 一. YOLO V5我来啦 1. 前期准备 yolo V5项目下载 项目的github地址项目的gitee地址 使用git 克隆下来到项目目录下面就好 环境配置 在yolov5的文件下面有一个 requirements.txt文件,这里就是环境依赖的说明。 这里我以 vs…

DP学习之解码方法

DP学习第二篇之解码方法 91. 解码方法 - 力扣&#xff08;LeetCode&#xff09; 一. 题目解析 二. 题解 算法原理及代码 状态表示 tips: 经验题目要求。以i位置为结尾&#xff0c;。。。 dp[i]: 以i位置为结尾时&#xff0c;解码方法的总数 状态转移方程 tips: 用之前或…

5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议

5.3.2 因特网的路由协议&#xff08;二&#xff09;基于距离向量算法的RIP协议 一、RIP协议概念 RIP是Routing Information Protocol缩写&#xff0c;又称为路由信息协议&#xff0c;是最先得到应用的内部网关协议&#xff0c;RIP作为一个常在小型互联网中使用的路由信息协议…

【mmcls】mmdet中使用mmcls的网络及预训练模型

mmcls现在叫mmpretrain&#xff0c;以前叫mmclassification&#xff0c;这里为了统一称为mmcls。在基于MM框架的下游任务&#xff0c;例如检测(mmdetection)中可以使用mmcls中的backbone进行特征提取&#xff0c;但这就需要知道网络的参数以及输出特征的维度。本文简单介绍了在…

CDD诊断数据库的简单介绍

1. 什么是数据库? 数据库是以结构化方式组织的一个数据集合。 比如DBC数据库: Network nodes Display Rx Messages EngineState(0x123) 通过结构化的方式把网络节点Display里Rx报文EngineState(0x123)层层展开。这种方 式的好处是:层次清晰,结构分明,易于查找。 2. 什么…

ERROR: AddressSanitizer: heap-use-after-free on address

内存错误"heap-use-after-free"&#xff0c;这是因为在C中&#xff0c;当使用delete关键字释放对象的内存后&#xff0c;该对象仍然会保留指向已经被释放内存的指针。这个指针称为悬挂指针&#xff08;Dangling Pointer&#xff09;。如果我们试图访问已经被释放的内…

【Linux】15. 文件系统与软硬链接

1. 文件系统的引出 在之前的学习过程当中&#xff0c;我们知道当文件被打开后需要加载进内存&#xff0c;第一步为其创建struct file结构体描述其结构(操作系统需要管理被打开的文件&#xff1a;先描述再组织)&#xff0c;在通过进程当中的文件描述符指针指向文件描述符表&…

《机器学习算法竞赛实战》-chapter2问题建模

《机器学习算法竞赛实战》学习笔记&#xff0c;记录一下自己的学习过程&#xff0c;详细的内容请大家购买作者的书籍查阅。 问题建模 当参赛者拿到竞赛题目时&#xff0c;首先应该考虑的事情就是问题建模&#xff0c;同时完成基线(baseline)模型的pipeline搭建&#xff0c;从…