SpringBoot统一异常处理和统一返回格式

news/2025/3/15 6:32:36/

上篇博客我们讲解了使用AOP来进行统一的用户登录判断,其实像这种功能统一且使用较多的地方,都可以用AOP来处理,除了统⼀的⽤户登录判断之外,AOP 还可以实现:

  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计(在性能优化阶段,监控流量,接口的响应时间等甚至每个方法的响应时间,为整个项目的性能进行优化)
  • 统⼀的返回格式设置 (对于接口的返回格式,基本上都是code,message,data)
  • 统⼀的异常处理
  • 事务的开启和提交等

1.统一异常处理

我们写一个类来测试一下:

package com.example.springaop.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
//@RequestMapping("/ex")
public class ExceptionController {@RequestMapping("/test1")public boolean test1() {int a = 10/0;return true;}@RequestMapping("/test2")public boolean test2() {String str = null;System.out.println(str.length());return true;}@RequestMapping("/test3")public boolean test3() {throw new RuntimeException("测试运行时异常");}
}

运行后去浏览器访问对于的url测试:

 

 

可以在控制台看见对应的异常信息如果不进行统一异常处理的话,那么有些浏览器可能会将异常清楚的告知用户 

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件,具体实现代码如下:
package com.example.springaop.config;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;import java.util.HashMap;@ControllerAdvice// 控制器的通知,交给Spring管理
public class ErrorHandler {@ResponseBody// 表示返回的数据不是视图@ExceptionHandlerpublic Object error (Exception e) {// 根据我的方法参数来捕获异常,这里捕获的就是Exception异常HashMap<String, Object> result = new HashMap<>();result.put("success",0);result.put("code",-1);result.put("msg","内部异常");return result;}
}
  • ⽅法名和返回值可以⾃定义,其中最重要的是 @ExceptionHandler(Exception.class) 注解,括号里面的内容可以不写
  • 当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配
我们去浏览器测试一下异常

 

可以看到这些异常返回了相应的结果 

我们再来写一个捕获算数异常和空指针异常统一处理的方法

    @ResponseBody// 表示返回的数据不是视图@ExceptionHandlerpublic Object error (ArithmeticException e) {// 根据我的方法参数来捕获异常,这里捕获的就是算数ArithmeticException异常HashMap<String, Object> result = new HashMap<>();result.put("success",0);result.put("code",-2);result.put("msg","ArithmeticException。。。");return result;}@ResponseBody// 表示返回的数据不是视图@ExceptionHandlerpublic Object error (NullPointerException e) {// 根据我的方法参数来捕获异常,这里捕获的就是空指针异常HashMap<String, Object> result = new HashMap<>();result.put("success",0);result.put("code",-3);result.put("msg","NullPointerException。。。");return result;}

此时我们再来访问算数异常的url为test1,空指针异常为test2

 

此时就返回了我们设置的算数异常和空指针异常应该返回的数据 

这里有一个需要注意的点就是,如果我们给ExceptionController加上了拦截器

 这样我们再去访问的时候发现:

 

我们并未修改Exception Controller类中的任何代码,只是给他加了一个拦截器而已,那么这又是什么原因呢?

我们查看环绕通知的代码即可发现,不论是什么异常,经过环绕通知最后都变成了运行时异常

解决办法就是把这里修改一下,把连接点里面的异常直接抛出,就像下面这样:

 此时我们再去浏览器测试:

 

算数异常和空指针异常又正常显示了

代码之间经常会互相影响,出现错误后,一定要有耐心的寻找错误,相信自己一定可以找到的

2.统⼀的返回格式

1. 为什么需要统⼀数据返回格式

  • ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。
  • 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就⾏了,因为所有接⼝都是这样返回的。
  • 有利于项⽬统⼀数据的维护和修改。
  • 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容

2. 统⼀数据返回格式的实现

统⼀的数据返回格式可以使⽤ @ControllerAdvice + ResponseBodyAdvice 的⽅式实现,具体实现代码如下:

 

@ControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice {/*** 内容是否需要重写(通过此⽅法可以选择性部分控制器和⽅法进⾏重写)* 返回 true 表示重写*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}/*** ⽅法返回之前调⽤此⽅法*/@Overridepublic Object beforeBodyWrite(Object body, // 响应的正文内容MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {// 封装HashMap<String, Object> result = new HashMap<>();result.put("success",1);result.put("data",body);result.put("errorMsg","");return result;}
}

此时我们在访问user类中的url:

/*** 用户需要调用的一些方法*/
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {// 获取用户信息@RequestMapping("/get")public String getInfo() {log.info("获取信息...");return "获取信息";}// 注册@RequestMapping("/reg")public Boolean reg() {log.info("注册...");return true;}// 登录@RequestMapping("/log")public Boolean log(HttpServletRequest request, String username, String password) {log.info("login...");// 判断用户名和密R码,如果有任意一个为空,那么就不能登陆成功if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}// 此时判断用户名和密码是否正确// 假装判断一下if (!"admin".equals(username) || !"admin".equals(password)) {return false;}// 此时用户名和密码校验通过// 参数true表示,如果没有存放Session,那么需要创建一个Session来存放当前登录的用户HttpSession session = request.getSession(true);session.setAttribute("username",username);return true;}}

 

 

 可以看到,我们访问get的url时出错了,因为它返回的数据是String类型

原因在这里可以看到:SpringBoot 使用 beforeBodyWrite 实现统一的接口返回类型_一梦喂马.的博客-CSDN博客

那么该如何解决呢?

再加一个判断方法,当它返回的数据为String类型时,手动按照Json的数据格式返回

 异常使用@SneakyThrows注解,重新去浏览器访问:

可以看到访问成功了,不论访问的是那个页面,成功与否,返回的数据都是我们刚才设置的统一格式

3.总结

  • 自定义拦截器使⽤ WebMvcConfigurer+ HandlerInterceptor来实现,
  • 统⼀异常处理使⽤ @ControllerAdvice + @ExceptionHandler 来实现,
  • 统⼀返回值处理使⽤@ControllerAdvice + ResponseBodyAdvice 来处理
下篇见~

 

 


http://www.ppmy.cn/news/991333.html

相关文章

建造者设计模式 + 高阶函数 => DSL

该设计模式适用于创建复杂对象&#xff0c;该复杂对象通常是由各个部分的子对象用一定的算法或者步骤构成&#xff0c;针对每个子对象内部算法和步骤通常是稳定的&#xff0c;但是该复杂对象的确实由于不同的需求而选择使用不同的子对象进行组装。对于构建该复杂的对象&#xf…

MD-MTSP:成长优化算法GO求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、成长优化算法GO 成长优化算法&#xff08;Growth Optimizer&#xff0c;GO&#xff09;由Qingke Zhang等人于2023年提出&#xff0c;该算法的设计灵感来源于个人在成长过程中的学习和反思机制。学习是个人通过从外部世界获取知识而成长的过程&#xff0c;反思是检查个体自…

阿里用户序列建模MIMN

Practice on Long Sequential User Behavior Modeling for Click-Through Rate Prediction 摘要 对于序列建模&#xff0c;实践了机器学习算法与在线服务协同设计的CTR预测系统&#xff0c;理论上可以处理无限长的用户序列。 从服务系统的角度来看&#xff0c;通过设计一个单…

深度学习之梯度下降算法

0.1 学习视频源于&#xff1a;b站&#xff1a;刘二大人《PyTorch深度学习实践》 0.2 本章内容为自主学习总结内容&#xff0c;若有错误欢迎指正&#xff01; 1 线性模型 1.1 通过简单的线性模型来举例&#xff1a; 1.2 如图&#xff0c;简单的一个权重的线性模型&#xff0c…

数据结构:栈和队列的实现和图解二者相互实现

文章目录 写在前面栈什么是栈栈的实现 队列什么是队列队列的实现 用队列实现栈用栈模拟队列 写在前面 栈和队列的实现依托的是顺序表和链表&#xff0c;如果对顺序表和链表不清楚是很难真正理解栈和队列的 下面为顺序表和链表的实现和图解讲解 手撕图解顺序表 手撕图解单链表 …

二维深度卷积网络模型下的轴承故障诊断

1.数据集 使用凯斯西储大学轴承数据集&#xff0c;一共有4种负载下采集的数据&#xff0c;每种负载下有10种 故障状态&#xff1a;三种不同尺寸下的内圈故障、三种不同尺寸下的外圈故障、三种不同尺寸下的滚动体故障和一种正常状态 2.模型&#xff08;二维CNN&#xff09; 使…

msvcp120.dll丢失的解决方法?有什么解决方法比较推荐?

第一&#xff1a;介绍几种可能导致msvcp120.dll文件丢失或损坏的原因 损坏的程序安装&#xff1a;在安装某个程序时&#xff0c;可能会出现意外中断或其他错误&#xff0c;导致msvcp120.dll文件未能正确地被安装或被破坏。这可能是由于软件安装过程中的错误、病毒感染或硬件问题…

04mysql查询之多表查询

# 1.显示所有员工的姓名&#xff0c;部门号和部门名称。 SELECT last_name,e.department_id,department_name FROM employees e LEFT JOIN departments d ON e.department_id d.department_id # 2.查询90号部门员工的job_id和90号部门的location_id SELECT e.job_id,d.locat…