Spring Boot 全局异常处理

Spring Boot 全局异常处理

ErrorCode.java (此枚举类中包含了异常的唯一标识、HTTP 状态码以及错误信息)

这个类的主要作用就是统一管理系统中可能出现的异常,比较清晰明了。但是,可能出现的问题是当系统过于复杂,出现的异常过多之后,这个类会比较庞大。有一种解决办法:将多种相似的异常统一为一个,比如将用户找不到异常和订单信息未找到的异常都统一为“未找到该资源”这一种异常,然后前端再对相应的情况做详细处理。

import org.springframework.http.HttpStatus;public enum ErrorCode {/*** 注意写错误码的几点:* 1.是 public enum 不是 public class* 2.只需要写get方法和全构造* 3.错误参数构造之间用逗号隔开*/RESOURCE_NOT_FOUND(1001, HttpStatus.NOT_FOUND, "未找到该资源"),REQUEST_VALIDATION_FAILED(1002, HttpStatus.BAD_REQUEST, "请求数据格式验证失败");private final int code;private final HttpStatus status;private final String message;ErrorCode(int code, HttpStatus status, String message) {this.code = code;this.status = status;this.message = message;}public int getCode() {return code;}public HttpStatus getStatus() {return status;}public String getMessage() {return message;}@Overridepublic String toString() {return"ErrorCode{" +"code=" + code +", status=" + status +", message='" + message + '\'' +'}';}
}

ErrorResponse.java(返回给客户端具体的异常对象)

这个类作为异常信息返回给客户端,里面包括了当出现异常时我们想要返回给客户端的所有信息。

import org.springframework.util.ObjectUtils;import java.time.Instant;
import java.util.HashMap;
import java.util.Map;public class ErrorResponse {// 唯一标示异常的 codeprivate int code;// HTTP状态码private int status;// 错误的具体信息private String message;// 错误路径private String path;// 发生错误的时间戳private Instant timestamp;private HashMap<String, Object> data = new HashMap<String, Object>();public ErrorResponse() {}public ErrorResponse(BaseException ex, String path) {this(ex.getError().getCode(), ex.getError().getStatus().value(), ex.getError().getMessage(), path, ex.getData());}public ErrorResponse(int code, int status, String message, String path, Map<String, Object> data) {this.code = code;this.status = status;this.message = message;this.path = path;this.timestamp = Instant.now();if (!ObjectUtils.isEmpty(data)) {this.data.putAll(data);}}// 省略 getter/setter 方法@Overridepublic String toString() {return"ErrorReponse{" +"code=" + code +", status=" + status +", message='" + message + '\'' +", path='" + path + '\'' +", timestamp=" + timestamp +", data=" + data +'}';}
}

BaseException.java(继承自 RuntimeException 的抽象类,可以看做系统中其他异常类的父类)

系统中的异常类都要继承自这个类

public abstract class BaseException extends RuntimeException {private final ErrorCode error;private final HashMap<String, Object> data = new HashMap<>();public BaseException(ErrorCode error, Map<String, Object> data) {super(error.getMessage());this.error = error;if (!ObjectUtils.isEmpty(data)) {this.data.putAll(data);}}protected BaseException(ErrorCode error, Map<String, Object> data, Throwable cause) {super(error.getMessage(), cause);this.error = error;if (!ObjectUtils.isEmpty(data)) {this.data.putAll(data);}}public ErrorCode getError() {return error;}public Map<String, Object> getData() {return data;}}

ResourceNotFoundException.java (自定义异常)

可以看出通过继承 BaseException 类自定义异常会变的非常简单!

import java.util.Map;public class ResourceNotFoundException extends BaseException {public ResourceNotFoundException(Map<String, Object> data) {super(ErrorCode.RESOURCE_NOT_FOUND, data);}
}

GlobalExceptionHandler.java(全局异常捕获)

定义了两个异常捕获方法。

这里再说明一下,实际上这个类只需要 handleAppException() 这一个方法就够了,因为它是本系统所有异常的父类。只要是抛出了继承 BaseException 类的异常后都会在这里被处理。

import com.twuc.webApp.web.ExceptionController;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;@ControllerAdvice(assignableTypes = {ExceptionController.class})
@ResponseBody
public class GlobalExceptionHandler {// 也可以将 BaseException 换为 RuntimeException// 因为 RuntimeException 是 BaseException 的父类@ExceptionHandler(BaseException.class)public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {ErrorReponse representation = new ErrorReponse(ex, request.getRequestURI());return new ResponseEntity<>(representation, new HttpHeaders(), ex.getError().getStatus());}@ExceptionHandler(value = ResourceNotFoundException.class)public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {ErrorReponse errorReponse = new ErrorReponse(ex, request.getRequestURI());return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorReponse);}
}

当抛出了ResourceNotFoundException异常,会被handleResourceNotFoundException()方法捕获。因为 @ExceptionHandler 捕获异常的过程中,会优先找到最匹配的。

补充内容:

  1. @ControllerAdvice可以指定捕获异常的控制器范围,比如这里的assignableTypes=ExceptionController.class,表示只处理ExceptionController抛出的异常。不指定的话默认对所有controller有效。

  2. 可以在全局异常处理方法中获取更多上下文信息,如请求参数,用户信息等,方便异常处理和日志记录。

  3. 可以定义不同的全局异常处理类拦截不同范围的异常,比如定义一个全局未捕获异常处理器,一个控制器异常处理器等。

    /*** 定义一个全局未捕获异常处理器类* 该异常处理器的优先级低于其他具体的异常处理方法* 可以配合其他具体异常处理方法,处理未覆盖到的异常情况* 可以保证系统兜底捕获所有异常,避免未处理的异常直接抛出到用户*/
    @ControllerAdvice
    public class GlobalExceptionHandler {// 通过Exception.class参数捕获一切Exception类及其子类的异常@ExceptionHandler(value = Exception.class)  public ResponseEntity<String> handleException(Exception e){// 处理未捕获的异常return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); }}
    
    /*** 在控制器类中定义异常处理方法,加上@ExceptionHandler注解* 与全局异常处理器配合使用,控制器内的异常处理器优先级更高* 这样可以在控制器层面处理控制器范围内的特定异常* 所以Controller中的异常处理器可以更加具体地处理控制器业务场景下的异常情况*/
    @RestController
    public class UserController {// 该方法可以捕获在本控制器类中的UserNotFoundException异常@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<String> handleUserNotFound(UserNotFoundException e) {// ...return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User not found");}}
    
  4. 如上例可以使用@ExceptionHandler在控制器内处理控制器范围内的异常,与全局异常处理器配合使用。

  5. 正确设置HTTP状态码很重要,比如404找不到资源等,有助于客户端判断异常情况。

  6. 可以在异常处理方法中获取bindingResult,分析参数校验的错误信息。

  7. 返回给客户端的错误信息体中可以包含代码、信息等字段,以便客户端准确判断和处理。

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

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

相关文章

【C++】list 模拟笔记

文章目录 list定义结点类&#xff08;list_node&#xff09;为什么封装迭代器为类 &#xff1f;库里面模板多参数的由来 &#xff1f;为什么普通迭代器不能隐式类型转换成const迭代器&#xff1f;迭代器位置指向及其返回值和整体代码 list list 和前面学习的 string 和 vector …

微信小程序使用ECharts的示例详解

目录 安装 ECharts 组件使用 ECharts 组件图表延迟加载 echarts-for-weixin 是 ECharts 官方维护的一个开源项目&#xff0c;提供了一个微信小程序组件&#xff08;Component&#xff09;&#xff0c;我们可以通过这个组件在微信小程序中使用 ECharts 绘制图表。 echarts-fo…

excel中单行换成多行

今天碰以下情况&#xff1a; 这在excel表中是在一个单元格&#xff0c;现在需要对其进行转换&#xff0c;将一个单元格换成多行 步骤&#xff1a; 1.删除换行符&#xff0c;添加一个逗号 2.选择数据-分列-分隔字符-逗号-确定 3.复制上述数据&#xff0c;选择性粘贴-转置 完…

MemberApplication

目录 1 MemberApplication 1.1 /// 发送验证码 1.1.1 //验证码超时时间 1.2 /// 验证码验证 MemberApplication /// 发送验证码

2816. 判断子序列

题目链接&#xff1a; 自己的做法&#xff1a; #include <bits/stdc.h>using namespace std;const int N 1e5 10; int a[N], b[N]; int main() {int n, m;bool flag true;scanf("%d%d", &n, &m);for (int i 0; i < n; i) scanf("%d"…

笙默考试管理系统-MyExamTest(10)

笙默考试管理系统-MyExamTest&#xff08;10&#xff09; 目录 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙默考试管理系统-MyExamTest 五、 笙默考试管理系统-MyExamTest 笙默考试管理系统-MyExa…

哈希:探索快速的数据存储和搜索方法

哈希&#xff1a;探索快速的数据存储和搜索方法 哈希表作为一种高效的数据存储结构&#xff0c;可以使数据的存储位置与关键码之间建立一一映射的关系&#xff0c;从而加快元素的搜索速度。然而&#xff0c;哈希方法也面临着哈希冲突的问题&#xff0c;即不同的关键字通过相同…

dxf怎么转换成PDF格式?转换方法其实很简单

PDF文件是一种可靠的文件格式&#xff0c;可以在各种操作系统和软件上打开和查看。而dxf是CAD文件的一种格式&#xff0c;打开它一般都是需要相关的操作软件才能打开&#xff0c;不是特别方便&#xff0c;将dxf文件转换成PDF格式就可以很好的解决这一问题&#xff0c;下面教大家…

解锁新技能《基于logback的纯java版本SDK实现》

开源SDK&#xff1a; <!--Java通用日志组件SDK--> <dependency><groupId>io.github.mingyang66</groupId><artifactId>oceansky-logger</artifactId><version>4.3.6</version> </dependency> <!-- Java基于logback的…

Kafka - Primie Number of Partitions Issue Consumer Group Rebalance

文章目录 生产者&#xff1a;将数据写入 Kafka 的客户端。 消费者&#xff1a;从 Kafka 中读取数据的客户端。 Topic&#xff1a;Kafka 中用于组织和存储数据的逻辑概念&#xff0c;类似于数据库表。 Record&#xff1a;发送到 Topic 的消息称为 Record。 Partition&#x…

electron 安装失败,Electron failed to install correctly

我的电脑是mac&#xff0c;这个问题上网找了很多解决办法试了一下都不行&#xff0c;删除重装也不太行&#xff0c;但是单独安装是可以的 install electron执行完后再启动就可以啦 npm start下面的不太行 1.切到node_modules/electron执行如下指令 node install.js还有 nod…

图像清晰度判断

FFT&#xff08;Fast Fourier Transform&#xff09;是一种常用的频域分析工具&#xff0c;可以将图像从空间域转换到频域。在频域中&#xff0c;高频对应了图像的细节部分&#xff0c;低频对应了图像的大致形状。因此&#xff0c;我们可以通过计算图像在高频部分的能量来评估图…

List有值二次转换给其他对象报null

List<PlatformUsersData> listData platformUsersMapper.selectPlatformUserDataById(data); users.setPlatformUsersData(listData);为什么listData 有值&#xff0c;users.getPlatformUsersData&#xff08;&#xff09;仍然为空在这段代码中&#xff0c;我们假设listD…

NLP(六十)Baichuan-13B-Chat模型使用体验

2023年7月11日&#xff0c;百川智能正式发布参数量130亿的通用大语言模型Baichuan-13B-Base、对话模型Baichuan-13B-Chat及其INT4/INT8两个量化版本。   本文将介绍大模型BaiChuan-13B-Chat的使用体验&#xff0c;其HuggingFace网址为&#xff1a;https://huggingface.co/bai…

【团队协作开发】IDEA中Git新建自己的dev工作分支,合并到master主分支教程(极其简单,新手)

文章目录 一、创建新dev工作分支二、push到自己的远程dev工作分支三、工作分支合并到master主分支1、先切换到master主分支2、将远程工作dev分支的内容merge到当前master分支中3、将merge提交到远程master分支 一、创建新dev工作分支 创建完新dev分支以后将默认切换到新dev分支…

csdn 机器人回答 Spring 设计模式

Spring框架中使用了多种设计模式&#xff0c;包括但不限于&#xff1a; 依赖注入&#xff08;DI&#xff09;模式&#xff1a;通过DI模式&#xff0c;Spring框架可以将对象之间的依赖关系从代码中解耦出来&#xff0c;使得代码更加灵活、可维护。 控制反转&#xff08;IoC&…

FFmpeg5.0源码阅读—— avcodec_send_frame avcodec_receive_packet

摘要&#xff1a;本文主要描述了FFmpeg中用于编码的接口的具体调用流程&#xff0c;详细描述了该接口被调用时所作的具体工作。   关键字&#xff1a;ffmpeg、avcodec_send_frame、avcodec_receive_packet   读者须知&#xff1a;读者需要了解FFmpeg的基本使用流程&#xf…

如何理解自动化

目录 1.如何定义自动化 2.自动化给人类带来的福利 3.如何学习自动化 4.自动化潜在的危害 1.如何定义自动化 自动化是指利用计算机、机械、电子技术和控制系统等现代科学技术手段&#xff0c;对各种工业、商业、农业和日常生活中的操作和过程进行自动控制和执行的过程。它旨在…

如何使用Java开发iOS应用?

使用Java开发iOS应用并不直接支持&#xff0c;因为iOS平台采用的是Objective-C和Swift作为主要的开发语言。然而&#xff0c;有一些跨平台工具和框架可以让你使用Java来开发跨平台的移动应用&#xff0c;包括iOS。 以下是一些可以考虑的选项&#xff1a; React Native&#xf…

Vc - Qt - 自定义ComboBox

示例代码创建了一个名为ComboBoxWidget的自定义QWidget类&#xff0c;并在initUI方法中创建了一个垂直布局。然后将一个只读的QLineEdit和一个QPushButton添加到布局中。当按钮被点击时&#xff0c;会调用showMenu方法&#xff0c;该方法创建一个QMenu并添加选项。每个选项连接…