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

news/2024/11/23 3:54:38/

场景

在编写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.ppmy.cn/news/70403.html

相关文章

typescript里面正则的使用

以下是一些常用的正则表达式元字符&#xff1a; ^&#xff1a;匹配字符串的开头。$&#xff1a;匹配字符串的结尾。.&#xff1a;匹配任意单个字符&#xff0c;除了换行符。*&#xff1a;匹配前面的字符零次或多次。&#xff1a;匹配前面的字符一次或多次。?&#xff1a;匹配…

无效数据处理之道:Linux系统编程C/C++实践探索

无效数据处理之道&#xff1a;Linux系统编程C/C实践探索 一、引言1.1 无效数据的来源1. 用户输入2. 硬件故障3. 软件错误 1.2 检测和处理无效数据1. 数据验证2. 异常处理3. 断言4. 内存管理 1.3 无效数据的预防和处理最佳实践1. 设计健壮的输入验证机制2. 错误处理和恢复机制3.…

Vue3(6) Transition

目录 组件 基于CSS的过渡效果 JavaScript钩子 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被…

模拟封装C标准库

文章目录 1. 准备工作2. my_fopen3. my_fwrite4. my_fclose和my_fflush5. syncfs 1. 准备工作 举个例子&#xff1a; 这里我们要实现my_fopen&#xff0c;my_fwrite和my_fclose这三个函数&#xff0c;并封装MyFILE。 这是MyFILE的封装&#xff0c;然后我们把三个函数接口完成…

JDK8之Optional类

Optional 类是 Java 8 引入的一个容器类&#xff0c;它主要解决了空指针异常问题。在 Java 中&#xff0c;如果我们访问一个空引用&#xff08;null&#xff09;&#xff0c;就会抛出 NullPointerException 异常。使用 Optional 类可以有效地避免这个问题。 Optional 类可以存…

I3C仿真:PGY I3C-EX-PD使用

目录 简述 环境配置 设备基本介绍 状态指示灯介绍 软件功能基本介绍 基本框架示意图 基本功能说明 Toolbar Controller Auxiliary Config Wave form Func View Decoded result SelectedFrame 使用教程 连接设备 查看网络 添加设备 删除与修改设备参数 保存…

前端开发之函数式编程实践 | 京东云技术团队

作者&#xff1a;京东科技 牛志伟 函数式编程简介 常见应用场景 1、ES6中的map、filter、reduce等函数 [1,2,3,4,5].map(x > x * 2).filter(x > x > 5).reduce((p,n) > p n);2、React类组件 -> 函数式组件hooks、Vue3中的组合式API 3、RxJS、Lodash和Ramd…

Xcode安装与配置

安装Xcode Xcode需要macOS系统上安装&#xff0c;截止到2020年1月31日&#xff0c;最新版本为11.3.1。你可以选择在苹果开发者网站下载Xcode安装文件或去苹果应用市场安装&#xff0c;无论哪种方式安装Xcode&#xff0c;都需要有一个自己的Apple ID&#xff0c;具体的申请注册流…