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

news2024/11/15 23:49:16

上篇博客我们讲解了使用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// 表示返回的数据不是视图
    @ExceptionHandler
    public 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// 表示返回的数据不是视图
    @ExceptionHandler
    public 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// 表示返回的数据不是视图
    @ExceptionHandler
    public 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 表示重写
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    /**
     * ⽅法返回之前调⽤此⽅法
     */
    @Override
    public 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.coloradmin.cn/o/811494.html

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

相关文章

8.事件对象

8.1获取事件对象 ●事件对象是什么 也是个对象&#xff0c;这个对象里有事件触发时的相关信息 例如&#xff1a;鼠标点击事件中&#xff0c;事件对象就存了鼠标点在哪个位置等信息 ●使用场景 可以判断用户按下哪个键&#xff0c;比如按下回车键可以发布新闻 可以判断鼠标点击…

【动态规划】子数组系列

文章目录 动态规划&#xff08;子数组系列&#xff09;1. 最大子数组和2. 环形子数组的最大和3. 乘积最大子数组4. 乘积为正的最长子数组的长度5. 等差数列划分6. 最长湍流子数组7. 单词拆分8. 环形字符串中的唯一的子字符串 动态规划&#xff08;子数组系列&#xff09; 1. 最…

ETHERCAT转CCLINK网关连接ethercat转换器

你们有没有遇到这样的问题&#xff1f;在生产管理系统中&#xff0c;数据互联互通是非常重要的&#xff0c;但ETHERCAT和CCLINK这两个协议之间的通讯一直是个大问题。今天&#xff0c;我给大家带来了一个好消息——捷米JM-ECT-CCLK&#xff0c;这是一款让各种CCLINK总线和ETHER…

WEB:php_rce

背景知识 Linux命令 thinkPHPv5漏洞 题目 打开页面&#xff0c;页面显示为thinkphp v5的界面&#xff0c;可以判断框架为thinkPHP&#xff0c;可以去网上查找相关的漏洞 由题目可知&#xff0c;php rec是一个通过远程代码执行漏洞来攻击php程序的一种方式 因为不知道是php版…

三数之和——力扣15

文章目录 题目描述法一 双指针排序 题目描述 法一 双指针排序 class Solution{ public:vector<vector<int>> threeSum(vector<int>& nums){int nnums.size();vector<vector<int>> ans;sort(nums.begin(), nums.end());for(int first0;first&…

项目实战 — 消息队列(2){创建核心类}

目录 一、创建项目 二、创建核心类 &#x1f345; 1、 编写交换机类&#xff0c;Exchange &#x1f345; 2、编写存储消息的队列&#xff0c;MSGQueue &#x1f345; 3、编写绑定类&#xff0c;binding &#x1f345; 4、编写消息&#xff0c;Message 一、创建项目 二、创…

【Golang 接口自动化04】 解析接口返回JSON串

目录 前言 解析到结构体 json数据与struct字段是如何相匹配的呢&#xff1f; 解析到interface Go类型和JSON类型 实例代码 simpleJson 总结 资料获取方法 前言 上一次我们一起学习了如何解析接口返回的XML数据&#xff0c;这一次我们一起来学习JSON的解析方法。 JSO…

Mysql 索引失效

1、模糊查询%在前面&#xff0c;无法排序所以失效 2、函数计算 3、表达式计算length(NAME) 4、隐式转换 5、联合索引非最左匹配 6、or 必须都为索引列 事务特性 &#xff08;来自小林coding 事务隔离级别是怎么实现的&#xff1f; | 小林coding (xiaolincoding.com)&#…

基于Open3D的点云处理13-分割

平面分割&#xff08;基于RANSAC&#xff09; 使用RANSAC算法从点云中拟合平面&#xff1b; 接口&#xff1a;segment_plane 测试&#xff1a;Plane-segmentation import open3d as o3dpcd_point_cloud o3d.data.PCDPointCloud() pcd o3d.io.read_point_cloud(pcd_point_cl…

从零开始学Docker(二):启动第一个Docker容器

宿主机环境&#xff1a;RockyLinux 9 这个章节不小心搞成命令学习了&#xff0c;后面在整理成原理吧 Docker生命周期 拉取并启动Nginx容器 # 查找镜像 例如&#xff1a;nginx [root192 ~]# docker search nginx 我们可以看到&#xff0c;第一个时官方认证构建的nginx # 拉…

beego通过gorm访问mysql数据库

一、下载golang 二、解压下载包到C盘 三、配置golang系统环境变量 四、进入新建的工作目录C:\project下载并安装beego 五、将新生成的bee.exe所在的路径c:\project\bin加入到系统变量path里面 六、下载并安装mysql 例如在上图中&#xff0c; 选“No thanks,just start my down…

iOS开发-实现上下翻转轮播循环信息播报效果

iOS开发-实现上下翻转轮播循环信息播报效果 在开发中经常遇到需要实现轮播信息播报&#xff0c;例如以下效果 一、使用UITableView实现展示列表 UITableView 定义UITableView的实例&#xff0c;并让当前视图遵守UITableView的两个协议 property (nonatomic, weak, nullable)…

【Linux多线程】线程的互斥与同步(附抢票案例代码+讲解)

线程的互斥与同步 &#x1f4ab; 概念引入⭐️临界资源&#xff08;Critical Resource&#xff09;&#xff1a;&#x1f31f;临界区&#xff08;Critical Section&#xff09;&#xff1a;✨互斥&#xff08;Mutex&#xff09;&#xff1a; ⚡️结合代码看互斥☄️ 代码逻辑&a…

无涯教程-jQuery - Progressbar组件函数

小部件进度条功能可与JqueryUI中的小部件一起使用。一个简单的进度条显示有关进度的信息。一个简单的进度条如下所示。 Progressbar - 语法 $( "#progressbar" ).progressbar({value: 37 }); Progressbar - 示例 以下是显示进度条用法的简单示例- <!doctype …

appium的基本使用

appium的基本使用 一、appium的基本使用appium环境安装1、安装Android SDK 2、安装Appium3、安装手机模拟器4、Pycharm安装 appium-python-alicent5、连接appium和模拟器6、Python代码调用appium软件&#xff0c;appium软件在通过adb命令调用android操作系统&#xff08;模拟器…

如何在局域网外SSH远程访问连接到家里的树莓派?

文章目录 如何在局域网外SSH远程访问连接到家里的树莓派&#xff1f;如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpol…

详解Mybatis之分页插件【PageHelper】

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 Mybatis版本&#xff1a;3.5.6 文章目录 一. 什么是分页&#xff1f;二. 为什么使用分页&#xff1f;三. 如何设计一个Page类&#xff08;分…

【Python】Web学习笔记_flask(1)——getpost

flask提供的request请求对象可以实现获取url或表单中的字段值 GET请求 从URL中获取name、age两个参数 from flask import Flask,url_for,redirect,requestappFlask(__name__)app.route(/) def index():namerequest.args.get(name)agerequest.args.get(age)messagef姓名:{nam…

玩转LaTeX(四)【文献引入、自命令】

参考文献BibTeX: 使用最原始的文献导入&#xff1a;&#xff08;正文区&#xff09; \begin{document}%参考文献bibtex %一次管理一次使用 %参考文献格式&#xff1a; %\begin{thebibliography}{编号样本} % \bibitem[记号]{引用标志}文献条目1 % \b…

Java 程序员:本是为了跳槽刷完 1000 道真题,想不到被老板知道直接给我升职

同事&#xff1a;前阵子听说你要跳槽&#xff0c;现在准备得怎么样啊&#xff1f; 程序员 T&#xff1a;不跳了 同事&#xff1a;啊&#xff1f;为什么&#xff1f; 程序员 T&#xff1a;涨薪了呗&#xff1f; 同事&#xff1a;真的吗&#xff1f;涨了多少&#xff1f;你自…