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

上篇博客我们讲解了使用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 来处理
下篇见~

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习:混合高斯聚类GMM(求聚类标签)+PCA降维(3维降2维)习题

使用混合高斯模型 GMM&#xff0c;计算如下数据点的聚类过程&#xff1a; Datanp.array([1,2,6,7]) 均值初值为: μ1,μ21,5 权重初值为: w1,w20.5,0.5 方差: std1,std21,1 K2 10 次迭代后数据的聚类标签是多少&#xff1f; 采用python代码实现&#xff1a; from scipy import…

人工智能-Dlib+Python实现人脸识别(人脸识别篇)

人脸识别流程 人脸检测,人脸数据提取:首先是检测到人脸保存人脸数据:可以保存到mysql数据库中mysql数据库连接mysql数据库安装mysql数据库操作设置人脸数据标签:(人脸名字),保存到数据库打开摄像头,检测到人脸,提取人脸数据:人脸数据与数据库中的数据对比,1、人脸检…

【图论】Prim算法

一.介绍 Prim算法是一种用于解决最小生成树问题的贪心算法。最小生成树问题是指在一个连通无向图中找到一个生成树&#xff0c;使得树中所有边的权重之和最小。 Prim算法的基本思想是从一个起始顶点开始&#xff0c;逐步扩展生成树&#xff0c;直到覆盖所有顶点。具体步骤如下…

用合成数据训练托盘检测模型【机器学习】

想象一下&#xff0c;你是一名机器人或机器学习 (ML) 工程师&#xff0c;负责开发一个模型来检测托盘&#xff0c;以便叉车可以操纵它们。 ‌你熟悉传统的深度学习流程&#xff0c;已经整理了手动标注的数据集&#xff0c;并且已经训练了成功的模型。 推荐&#xff1a;用 NSDT设…

【业务功能篇60】Springboot + Spring Security 权限管理 【终篇】

4.4.7 权限校验扩展 4.4.7.1 PreAuthorize注解中的其他方法 hasAuthority&#xff1a;检查调用者是否具有指定的权限&#xff1b; RequestMapping("/hello")PreAuthorize("hasAuthority(system:user:list)")public String hello(){return "hello Sp…

深度学习入门教程(1):用神经网络预测糖尿病病例Predict Diabetes Cases with Neural Networks

本深度学习入门教程是在polyu HPCStudio 启发以及资源支持下进行的&#xff0c;在此也感谢polyu以及提供支持的老师。 大纲&#xff08;what will you learn from this project&#xff09; 1&#xff1a;What are neural networks&#xff1f; 2&#xff1a;Why use neural …

3D 渲染技巧-如何创建高质量写实渲染?

掌握创建高质量建筑渲染和任何 3D 渲染的艺术是一项复杂且需要技巧的工作&#xff0c;通常需要多年的经验和实践。实现逼真的结果需要仔细考虑众多因素&#xff0c;并避免可能导致缺乏真实性的假渲染效果的常见错误。 避免常见错误 - 提升渲染游戏的技巧 在追求创建真正逼真的…

从零开始学习CTF——CTF是什么

引言&#xff1a; 从2019年10月开始接触CTF&#xff0c;学习了sql注入、文件包含等web知识点&#xff0c;但都是只知道知识点却实用不上&#xff0c;后来在刷CTF题才发现知识点的使用方法&#xff0c;知道在哪里使用&#xff0c;哪里容易出漏洞&#xff0c;可是在挖src漏洞中还…

Appium+python自动化(二十四) - 元素等待(超详解)

思考 在自动化过程中&#xff0c;元素出现受网络环境&#xff0c;设备性能等多种因素影响。因此元素加载的时间可能不一致&#xff0c;从而会导致元素无法定位超时报错&#xff0c;但是实际上元素是正常加载了的&#xff0c;只是出现时间晚一点而已。那么如何解决这个问题呢&am…

【业务功能篇57】Springboot + Spring Security 权限管理 【上篇】

4.权限管理模块开发 4.1 权限管理概述 4.1.1 权限管理的意义 后台管理系统中&#xff0c;通常需要控制不同的登录用户可以操作的内容。权限管理用于管理系统资源&#xff0c;分配用户菜单、资源权限&#xff0c;以及验证用户是否有访问资源权限。 4.1.2 RBAC权限设计模型 …

Scratch 教程 之 如何四舍五入保留一个小数到指定的数位

有些时候&#xff0c;我们需要四舍五入一个多位小数到指定的位&#xff0c;但scratch并没有这个积木&#xff0c;怎么做呢&#xff1f;我来教你&#xff5e; 我们创建一个函数&#xff0c;需要时调用就行了&#xff5e; 如图&#xff0c;创建一个带参函数&#xff0c;勾选"…

wxwidgets Ribbon构建多个page与按钮响应

新建一个控制台应用程序&#xff0c;添加好头文件的依赖与lib库文件的依赖&#xff0c;修改属性&#xff1a; 将进入ribbon界面的文件与主界面的类分开&#xff1a; 1、RibbonSample.cpp #include "stdafx.h" #include "MyFrame.h" class MyApp : public…

微服务——Docker

docker与虚拟机的区别 首先要知道三个层次 硬件层:计算机硬件 内核层:与硬件交互&#xff0c;提供操作硬件的指令 应用层: 系统应用封装内核指令为函数&#xff0c;便于程序员调用。用户程序基于系统函数库实现功能。 docker在打包的时候直接把应用层的函数库也进行打包&a…

机器学习深度学习——softmax回归的简洁实现

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——softmax回归从零开始实现 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你…

状态机实现N位按键消抖

状态机实现N位按键消抖 1、原理 利用状态机实现按键的消抖&#xff0c;具体的原理可参考 (50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客 状态机简介&#xff1a; 状态机分类可以主要分为两类&#xff1a;moore和mealy 根据三段式状态机最后…

Virtualbox虚拟机中Ubuntu忘记密码

1、首先重新启动Ubuntu系统&#xff0c;鼠标快速点一下Virtualbox虚拟机窗口获取焦点&#xff0c;然后按住shift键&#xff0c;以调出grub启动菜单。 2、根据提示按下键盘E键进入编辑模式&#xff0c;向下移动光标&#xff0c;将如下"ro quiet splash $vt_handoff"部…

软件测试面试【证券项目公司】

这家公司是做证券项目的&#xff0c;约的9点钟&#xff0c;路程还是有点遥远&#xff0c;转了一趟公交两趟地铁&#xff0c;精力都花在了路上&#xff0c;感觉有点累&#xff0c;以下是今天得面试流程。 到公司前台给我了一张面试表&#xff0c;写完之后就是等待面试。一共面试…

GAMES101 笔记 Lecture13 光线追踪1

目录 Why Ray Tracing?(为什么需要光线追踪&#xff1f;)Basic Ray Tracing Algorithm(基础的光线追踪算法)Ray Casting(光线的投射)Generating Eye Rays(生成Eye Rays) Recursive(Whitted-Styled) Ray Tracing Ray-Surface Intersection(光线和平面的交点)Ray Rquation(射线方…

PC音频框架学习

1.整体链路 下行播放&#xff1a; App下发音源→CPU Audio Engine 信号处理→DSP数字信号处理→Codec DAC→PA→SPK 上行录音&#xff1a; MIC拾音→集成运放→Codec ADC→DSP数字信号处理→CPU Audio Engine 信号处理→App 2.硬件 CPU PCH DSP(可选) Codec PA SPKbox MIC…

spring项目中idea提示Application context not configured for this file

今天在重构项目的时候&#xff0c;碰到一个问题。就是在spring底下&#xff0c;有一个包里面的所有配置类&#xff0c;在idea的开发工具类底下提示&#xff0c;Application context not configured for this file&#xff0c;如图所示 一开始以为是警告&#xff0c;不予处理&am…