SpringBoot中@ControllerAdvice/@RestControlAdvice+@ExceptionHandler实现全局异常捕获与处理

news2024/10/7 15:26:24

场景

在编写Controller接口时,为避免接口因为未知的异常导致返回不友好的结果和提示。

如果不进行全局异常捕获则需要对每个接口进行try-catch或其他操作。

 

可以对Controller进行全局的异常捕获和处理,一旦发生异常,则返回通用的500响应码与通用错误提示。

并将异常发生的具体的文件、类、方法、行数信息记录到日志。

@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,

可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理。

需要配合@ExceptionHandler使用。当将异常抛到controller时,可以对异常进行统一处理。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主

实现

1、@RestControlAdvice是组合注解,由@ControllerAdvice和@ResponseBody组成。

 

@ControllerAdvice 提供了多种指定Advice规则的定义方式,默认什么都不写,则是Advice所有Controller,

 

当然你也可以通过下列的方式指定规则。

通过basePackages指定只对哪些包路径下生效。

也可以通过指定注解来匹配,比如我自定了一个 @CustomAnnotation 注解,

我想匹配所有被这个注解修饰的 Controller, 可以这么写:@ControllerAdvice(annotations= {CustomAnnotation.class})

这里不做任何规则限制,对所有的controller生效。

2、新建全局异常捕获类,注意与启动类在同包路径下,不然不生效需要在启动类中指定包路径。

import com.badao.demo.common.AjaxResult;
import com.badao.demo.constant.Constants;
import com.badao.demo.constant.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//注意引入包的路径
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    {
        StackTraceElement stackTrace = e.getStackTrace()[0];
        String methodName = stackTrace.getMethodName();
        String fileName = stackTrace.getFileName();
        String className = stackTrace.getClassName();
        int lineNumber = stackTrace.getLineNumber();
        log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage());
        return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR);
    }
}

这里使用@ExceptionHandler(Exception.class)

对所有的Exception进行捕获,然后统一返回封装的AjaxResult结果类

import com.badao.demo.constant.HttpStatus;
import com.badao.demo.utils.StringUtils;

import java.util.HashMap;

/**
 * 操作消息提醒
 *
 */
public class AjaxResult extends HashMap<String, Object>
{
    private static final long serialVersionUID = 1L;

    /** 状态码 */
    public static final String CODE_TAG = "code";

    /** 返回内容 */
    public static final String MSG_TAG = "msg";

    /** 数据对象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     */
    public AjaxResult(int code, String msg)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg)
    {
        return new AjaxResult(code, msg, null);
    }
}

然后状态码和提示消息是封装的常量类

状态码常量类

public class HttpStatus
{
    /**
     * 操作成功
     */
    public static final int SUCCESS = 200;

    /**
     * 对象创建成功
     */
    public static final int CREATED = 201;

    /**
     * 请求已经被接受
     */
    public static final int ACCEPTED = 202;

    /**
     * 操作已经执行成功,但是没有返回数据
     */
    public static final int NO_CONTENT = 204;

    /**
     * 资源已被移除
     */
    public static final int MOVED_PERM = 301;

    /**
     * 重定向
     */
    public static final int SEE_OTHER = 303;

    /**
     * 资源没有被修改
     */
    public static final int NOT_MODIFIED = 304;

    /**
     * 参数列表错误(缺少,格式不匹配)
     */
    public static final int BAD_REQUEST = 400;

    /**
     * 未授权
     */
    public static final int UNAUTHORIZED = 401;

    /**
     * 访问受限,授权过期
     */
    public static final int FORBIDDEN = 403;

    /**
     * 资源,服务未找到
     */
    public static final int NOT_FOUND = 404;

    /**
     * 不允许的http方法
     */
    public static final int BAD_METHOD = 405;

    /**
     * 资源冲突,或者资源被锁
     */
    public static final int CONFLICT = 409;

    /**
     * 不支持的数据,媒体类型
     */
    public static final int UNSUPPORTED_TYPE = 415;

    /**
     * 系统内部错误
     */
    public static final int ERROR = 500;

    /**
     * 接口未实现
     */
    public static final int NOT_IMPLEMENTED = 501;
}

消息常量类

public class Constants {
    //请求响应常量
    public static final String NO_API_CODE = "请求码不能为空";
    public static final String ILLEGAL_API_CODE = "请求码不存在";
    public static final String SERVER_ERROR = "服务器内部错误,请联系管理员!";
    public static final String CALL_TOO_OFEN = "请求太频繁";
}

然后将异常发生的具体信息记录到日志中,异常的信息来源可以打断点获取和自定义需要获取的内容

 

2、这里是对Exception类型进行通配,如果需要对指定的异常类型或者自定义类型就行捕获,还可以

import com.badao.demo.common.AjaxResult;
import com.badao.demo.constant.Constants;
import com.badao.demo.constant.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//注意引入包的路径
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    {
        StackTraceElement stackTrace = e.getStackTrace()[0];
        String methodName = stackTrace.getMethodName();
        String fileName = stackTrace.getFileName();
        String className = stackTrace.getClassName();
        int lineNumber = stackTrace.getLineNumber();
        log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage());
        return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(HttpStatus.BAD_REQUEST, message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }
}

这里注意引入包的路径,不然会出现不生效的情况。

这块还可参考

SpringBoot+@Validate+全局异常拦截实现自定义规则参数校验(校验get请求参数不能为空且在指定枚举类型中):

SpringBoot+@Validate+全局异常拦截实现自定义规则参数校验(校验get请求参数不能为空且在指定枚举类型中)_霸道流氓气质的博客-CSDN博客

3、测试效果

手动编写一个除零异常并将controller中的try-catch去掉

 

接口统一响应,在日志中记录到具体信息

 

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

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

相关文章

洁净环境GMP 要求悬浮粒子 浮游菌 高效过滤器检漏验证所需仪器

lighthouse尘埃粒子计数器 北京中邦兴业是美国lighthouse中国区核心代理服务商&#xff0c;各类型号尘埃粒子计数器均有现货可发。常见尘埃粒子计数器类型&#xff1a;便携式&#xff08;台式&#xff09;、手持式、在线式、液体式。 客户现场 lighthouse浮游菌采样器 北京中…

nginx(七十五)nginx与Vary响应头细节探讨

一 Vary ① nginx与Vary有关联的地方 nginx源码分析处理Vary响应头的逻辑 CORS和缓存 gzip_vary 1) gzip_vary on 如果设置为开启2) 服务器返回数据时会在头部带上"Vary:Accept-Encoding"的标识3) 客户端便可以知道获取到的数据是否已经被压缩,默认为关闭 prox…

ros获取typec-usb摄像头

确保usb或者typec摄像头插在主机上,我这里是typec摄像头&#xff0c;使用了一个usb-typec转接头。 windows在设备管理器中查看是否存在端口&#xff0c;如果没有&#xff0c;可以使用下面的软件进行尝试。 链接&#xff1a;https://pan.baidu.com/s/1hxp3m68W6NjY-3D7q8rGnA 提…

Linux线程同步(1)——一个例子看懂为什么需要线程同步?

对于一个单线程进程来说&#xff0c;它不需要处理线程同步的问题&#xff0c;所以线程同步是在多线程环境下需要注意的问题。线程的主要优势在于&#xff0c;资源的共享性&#xff0c;譬如通过全局变量来实现信息共享&#xff0c;不过这种便捷的共享是有代价的&#xff0c;那就…

postman处理各种请求数据

1、后台request接收postman参数 2、后台单个参数接收postman 3、后台RequestParam参数接收postman 注意事项&#xff1a;情况一&#xff1a;全部都是单个字符串的 情况二&#xff1a;有可能是一个json对象序列化成字符串过来的&#xff0c;那么需要在form-data中设置 …

区间预测 | MATLAB实现QRCNN-LSTM卷积长短期记忆神经网络分位数回归时间序列区间预测

区间预测 | MATLAB实现QRCNN-LSTM卷积长短期记忆神经网络分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRCNN-LSTM卷积长短期记忆神经网络分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRCNN-LSTM分位数回…

Elasticsearch:如何在 Elasticsearch 中存储复杂的关系数据

在传统的数据库中&#xff0c;对数据关系的描述无外乎三种&#xff1a;一对一、一对多和多对多关系。 如果有关系相关的数据&#xff0c;我们一般在建表的时候加上主外键。 建立数据链接&#xff0c;然后在查询或者统计中通过 join 恢复或者补全数据&#xff0c;最后得到我们需…

Springboot +Flowable,设置流程变量的方式(二)

一.简介 为什么需要流程变量。 举个例子&#xff0c;假设有如下一个流程&#xff0c;截图如下&#xff1a; 这是一个请假流程&#xff0c;那么谁请假、请几天、起始时间、请假理由等等&#xff0c;这些都需要说明&#xff0c;不然领导审批的依据是啥&#xff1f;那么如何传递…

软件测试面试宝典,最常见的7个高频面试题(附答案,建议收藏)

收集了2022年所有黑马学员的面试题后&#xff0c;负责就业的黑马讲师们整理出了7个高频出现的面试题&#xff0c;一起来看看。 高频问题1&#xff1a;请自我介绍下&#xff1f; 高频问题2&#xff1a;请介绍下最近做过的项目&#xff1f; 高频问题3&#xff1a;请介绍下你印象…

2023 年第三届长三角高校数学建模 A 题 快递包裹装箱优化问题

2022 年&#xff0c;中国一年的包裹已经超过 1000 亿件&#xff0c;占据了全球快递事务量的一 半以上。近几年&#xff0c;中国每年新增包裹数量相当于美国整个国家一年的包裹数量&#xff0c; 十年前中国还是物流成本最昂贵的国家&#xff0c;当前中国已经建立起全世界最强大、…

利用jQuery做一个简单的猜数字游戏

目录 利用jQuery做一个简单的猜数字游戏 代码 效果 利用jQuery做一个简单的猜数字游戏 代码 <!DOCTYPE html> <html><head><title>键盘事件-猜数字</title><style>#body_style {background-color: #c7f5db;}#myDiv {position: absolu…

单片机GD32F303RCT6 (Macos环境)开发 (十七)—— i2c1从机中断接收发送数据

i2c1从机中断接收发送数据 1、将i2c1设置为从机模式&#xff0c;与树莓派连接。树莓派发送或者读取数据&#xff0c;gd32中断触发&#xff0c;从而接收数据或者向主机发送数据。 2、关于代码的宏定义配置 Application目录的Makefile中 ENABLE_I2C_TEST yes才会编译I2C1的相关…

为啥马斯克一边反对Open AI,一边又自己另搞AI

这事我过去就说过&#xff0c;我现在再老生常谈一次。 &#xff08;1&#xff09; 我过去说过一个事&#xff1a;汽车自己智能其实还不能做到真正的智能&#xff0c;必须车路人都智能了&#xff0c;智能汽车才能真正智能。 因为车路人智能&#xff0c;这本质是数字世界和数字世…

开源之夏 2023 | 与 Databend 一同探索云数仓的魅力

活动概览 开源之夏是由中科院软件所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动&#xff0c;旨在鼓励在校学生积极参与开源软件的开发维护&#xff0c;培养和发掘更多优秀的开发者&#xff0c;促进优秀开源软件社区的蓬勃发展&#xff0c;助力开源软件供应链…

基于AT89C52单片机的交通信号灯设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87772657 源码获取 主要内容&#xff1a; 本次设计所提出的一种基于单片机技术的简易计算器的方案&#xff0c;能更好的解决计算机计算的问题,随着数字生活的到来&…

eSIM-GSMA-1-EID规则定义

规范 SGP.02-v4.0.pdf Official Document SGP.02 - Remote Provisioning Architecture for Embedded UICC Technical Specification eid管理规则-SGP.29-1.0 EID Principles 国家编号机构发布 ICCID 的现有机制&#xff0c;以及后续的用户识别不受影响 EID 的核心目的是唯…

知识管理协同工具:助力企业综合信息规整化发展

随着信息化时代的来临&#xff0c;企业面临的信息越来越庞杂&#xff0c;如何管理和利用这些信息成为了企业发展的关键。知识管理作为一种新型的管理思想和方法&#xff0c;已经被广泛应用于企业管理中。知识管理工具是知识管理的重要手段之一&#xff0c;它可以帮助企业实现信…

STM32的功耗模式

按功耗由高到低排列&#xff0c; STM32 具有运行、睡眠、停止和待机四种工作模式。 低功耗各模式下芯片工作情况&#xff1a; 睡眠模式&#xff1a;仅关闭了内核时钟&#xff0c;内核停止运行&#xff0c;但其片上外设&#xff0c; CM4 核心的外设全都还照常 运行。有两种方式…

一款开源免费、非常好用的的SSH/SFTP客户端Electerm

electerm是一款基于electron开发的SSH/SFTP客户端&#xff0c;同时支持Linux、MAC、Windows操作系统&#xff0c;免费开源。 下载地址 Releases electerm/electerm (github.com) 点击下图箭头所指&#xff0c;下载windwos版本 安装 双击安装&#xff0c;等待安装完成 使用…

Melis4.0[D1s]:8.显示测试:图片格式和透明度

文章目录 1.准备素材图片1.1 测试图片像素格式的软件RawViewer.exe1.1.1 使用方法 1.2 自己生成测试图片 2.D1s显示引擎介绍&#xff08;不保证正确&#xff09;2.1 D1s 可以有2个独立的display device输出&#xff08;可以同时接2个显示器&#xff09;2.2 D1s 的 DISP0 有2个通…