[spring]SpringBoot拦截器 统一数据返回格式 统一异常处理

news2024/11/18 1:48:47

文章目录

  • 一. 拦截器
    • 1. 什么是拦截器
    • 2. 拦截器的使用
      • 自定义拦截器
      • 注册配置器
        • 拦截路径
    • 3. 适配器模式
  • 二. 统一数据返回格式
    • 异常处理
  • 三. 统一异常处理

一. 拦截器

1. 什么是拦截器

例如, 图书管理系统, 在访问图书list页面之前, 需要先进行登录, 如果没登录, 就需要进行强制跳转页面
那么我们就需要在每个接口中都进行判断是否进行了登录, 像这样重复的代码, SpringBoot就封装成了一个框架
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 拦截器的使用

在这里插入图片描述

自定义拦截器

定义一个接口类, 实现HandlerInterceptor接口, 根据需要重写里面的方法
在这里插入图片描述

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("目标方法执行前执行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("目标方法执行后执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("视图渲染完成后执行后执行");
    }
}

preHandle()方法, 返回true, 表示拦截后, 通过验证, 继续执行目标代码
返回false, 表示拦截后, 没有通过验证, 不能执行目标代码

登录验证:

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    private ObjectMapper objectMapper;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("校验用户是否登录");
        HttpSession session = request.getSession();
        UserInfo userInfo = (UserInfo) session.getAttribute(Constant.SESSION_USER_KEY);
        if(userInfo == null){
            log.warn("用户未登录");
            response.setStatus(401);
            return false;
        }
        log.info("用户登录检验通过");
        return true;
    }

注册配置器

自定义一个类, 实现WebMvcConfigurer, 重写addInterceptors方法

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //1. 注入拦截器对象
    @Autowired
    private LoginInterceptor loginInterceptor;
    //2. 重写方法, 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPathPatterns方法, 表示添加拦截的路径  /** 表示全部路径
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**");
    }
}

在这里插入图片描述
通过观察日志发现拦截成功

拦截路径

在这里插入图片描述
用户登录是, 我们希望对登录页面不进行拦截
在这里插入图片描述
但是发现并不好使…还是会拦截…(后面解决)
(测试返回不是字符串的类型, 会进行排除拦截, 但是String类型并没有拦截)
在这里插入图片描述

3. 适配器模式

在这里插入图片描述
在这里插入图片描述
使用场景:
前⾯学习的slf4j就使⽤了适配器模式,slf4j提供了⼀系列打印⽇志的api,底层调⽤的是log4j或者
logback来打⽇志,我们作为调⽤者,只需要调⽤slf4j的api就⾏了.

 /**
 * slf4j接⼝
*/
 interface Slf4jApi{
	 void log(String message);
 }
 /**
 * log4j 接⼝ 
*/
 class Log4j{
	 void log4jLog(String message){
 		System.out.println("Log4j打印:"+message);
 	}
 }
 /**
 * slf4j和log4j适配器
*/
 class Slf4jLog4JAdapter implements Slf4jApi{
	 private Log4j log4j;
	 public Slf4jLog4JAdapter(Log4j log4j) {
		 this.log4j = log4j;
	 }
 	@Override
 	public void log(String message) {
 		log4j.log4jLog(message);
	 }
 }
 /**
 * 客⼾端调⽤
 */
 public class Slf4jDemo {
	 public static void main(String[] args) {
		 Slf4jApi slf4jApi = new Slf4jLog4JAdapter(new Log4j());
 		slf4jApi.log("使⽤slf4j打印⽇志");
	 }
 }

在这里插入图片描述

二. 统一数据返回格式

后端需要对返回的数据进行封装, 告诉前端响应的结果
可以封装成一个Result枚举类进行返回, 里面包含自定义状态码, 错误信息, 和返回的数据
但是如果我们每一个都要修改成Result, 很麻烦, 所以SpringBoot给我们封装成了框架

使用@ControllerAdvice注解, 继承ResponseBodyAdvice接口, 重写接口中的方法

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @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) {
    	if(body instanceof Result<?>){
            return body;
        }
        return Result.success(body);
    }
}

supports⽅法: 判断是否要执⾏beforeBodyWrite⽅法.true为执⾏,false不执⾏. 通过该⽅法可以
选择哪些类或哪些⽅法的response要进⾏处理,其他的不进⾏处理.
beforeBodyWrite⽅法: 对response⽅法进⾏具体操作处理

异常处理

我们写一个返回不同参数的测试类

@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1() {
        return "t1";
    }
    @RequestMapping("/t2")
    public Boolean t2() {
        return true;
    }
    @RequestMapping("/t3")
    public Integer t3() {
        return 1;
    }
    @RequestMapping("/t4")
    public BookInfo t4() {
        return new BookInfo();
    }

}

测试t2t3t4都返回正常, 只有返回String会发生异常
在这里插入图片描述
通过异常信息, 进行打断点的方式, 阅读源码, 进行简单理解:
SpringMVC提供了很多对于返回结果的处理器, 会根据不同的返回类型, 进行调用不同的处理器
如果返回的是String, 那么进入的接口是String相关的处理器, 在这些方法中, 有一个方法会根据我们的统一返回数据, 对String进行封装, 二后面的方法, 接收参数的时候, 还是使用String进行接收的, 就会发生类型不匹配的情况
如果返回的不是String, 就不会发生类型不匹配的情况

我们进行统一数据返回处理后, 封装成了Result对象
但是如果返回String类型, 会发生类型不匹配异常, 所以我们在返回的时候, 不应该返回Result类型, 而应该是String, 所以使用SpringBoot内置提供的Jackson来实现信息序列化, writeValueAsString方法, 可以将接收到的对象参数, 按照JSON的方式, 就行转换, 然后通过String进行返回
在这里插入图片描述

在这里插入图片描述
此时, t1测试成功, 返回的就是JSON格式的字符串

上面登录页面, 返回的是String, 并且尽管我们去除拦截login接口, 还是会进行拦截
现在我们加上这个转JSON, 将异常处理了, 来测试一下:
在这里插入图片描述
没有进行拦截了, 并且正常返回String
原因在于: String类型, 如果不使用SpringBoot内置提供的Jackson来实现信息序列化, writeValueAsString方法, 就会抛异常, 那么Spring内部就会走到另一个"/error"路径, 而不再是"/user/login", 路径已经变了, 但是"/error"路径, 我们并没有进行拦截排除, 如果error这个路径会被拦截, 此时就会打印日志, 看起来好像是"/user/login"被拦截一样

三. 统一异常处理

上述的统一返回结果中, 返回的都是SUCCESS, 即使发生异常, 这显然是不合理的
那么, 我们就可以进行统一的异常处理, 前端在ajax中使用error接收

添加@ControllerAdvice注解, 并在处理异常的方法前加@ExceptionHandler注解
由于接口返回的是数据, 需要加上@ResponseBody

@ControllerAdvice注解:
在这里插入图片描述
所以@ControllerAdvice也可以实现将对象交给Spring处理

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
    @ExceptionHandler
    public Object handler(Exception e){
        return Result.fail(e.getMessage());
    }
}

也可以针对不同的异常, 返回不同的结果:
通过修改方法的参数类型

@ResponseBody
@ControllerAdvice
public class ErrorAdvice {
    @ExceptionHandler
    public Object handler(Exception e){
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Object handler1(NullPointerException e){
        return Result.fail("NullPointerException: " + e.getMessage());
    }
    @ExceptionHandler
    public Object handler2(ArithmeticException e){
        return Result.fail("ArithmeticException:" + e.getMessage());
    }
}

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

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

相关文章

SpringCloud入门(九)Feign实战应用和性能优化

一、Feign实战应用 Feign的客户端与服务提供者的controller代码非常相似&#xff1a; 有没有一种办法简化这种重复的代码编写呢&#xff1f; 方式一&#xff1a;继承 优点&#xff1a; 简单。实现了代码共享。 缺点&#xff1a;服务提供方、服务消费方紧耦合。参数列表中的注解…

【Docker】如何让docker容器正常使用nvidia显卡

首先确保宿主机正常安装了显卡驱动 nvidia-smi打印显卡信息如下&#xff1a; 安装nvidia-container-toolkit工具 sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo systemctl restart docker运行如下命令测试显卡是否在容器内可用 …

conda安装包离线安装环境

最近使用dokcerfile去创建镜像的时候&#xff0c;使用conda安装nvidia-cuda 总是失败&#xff0c;网络问题很大 可以通过离线的方式下载conda的安装包&#xff0c;然后放入文件中离线安装 通过以下命令得到安装包地址 conda search cudatoolkit --info 复制安装包地址到浏览器…

YOLOv5改进:Unified-loU,用于高品质目标检测的统一loU ,2024年8月最新IoU

💡💡💡现有IoU问题点:IoU (Intersection over Union)作为模型训练的关键,极大地显示了当前预测框与Ground Truth框之间的差异。后续研究者不断在IoU中加入更多的考虑因素,如中心距离、纵横比等。然而,仅仅提炼几何差异是有上限的;而且新的对价指数与借据本身存在潜在…

JMeter 实现 MQTT 协议压力测试 !

1. 下载jmeter&#xff0c;解压 https://jmeter.apache.org/download_jmeter.cgi 以 5.4.3 为例&#xff0c;下载地址&#xff1a; https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.zip linux下解压&#xff1a; unzip apache-jmeter-5.4.3.zip 2. 下载…

最新最全的阿里大模型面试真题!看到就是赚到

前言 随着人工智能技术的飞速发展&#xff0c;计算机视觉&#xff08;CV&#xff09;、自然语言处理&#xff08;NLP&#xff09;、搜索、推荐、广告推送和风险控制等领域的岗位越来越受到追捧&#xff0c;掌握大型模型技术已成为这些岗位的必备技能。然而&#xff0c;目前公开…

19.3 打镜像部署到k8s中,prometheus配置采集并在grafana看图

本节重点介绍 : 打镜像&#xff0c;导出镜像&#xff0c;传输到各个节点并导入运行该项目配置prometheus和grafana 打镜像 本地build docker build -t ink8s-pod-metrics:v1 .build过程 导出镜像 docker save ink8s-pod-metrics > ink8s-pod-metrics.tar 传输到各个node…

PCB基础

一、简介 PCB&#xff1a;printed circuit board&#xff0c;印刷电路板 主要作用&#xff1a;传输信号、物理支撑、提供电源、散热 二、分类 2.1 按基材分类 陶瓷基板&#xff1a;包括氧化铝、氮化铝、碳化硅基板等&#xff0c;具有优异的导热性&#xff0c;适用于高温和高…

论文复现:考虑电网交互的风电、光伏与电池互补调度运行(MATLAB-Yalmip-Cplex全代码)

论文复现:考虑电网交互的风电、光伏与电池储能互补调度运行(MATLAB-Yalmip-Cplex全代码) 针对风电、光伏与电化学储能电站互补运行的问题,已有大量通过启发式算法寻优的案例,但工程上更注重实用性和普适性。Yalmip工具箱则是一种基于MATLAB平台的优化软件工具箱,被广泛应用…

车辆零部件检测和分割数据集-车体数据集-yolo格式-yolov5-yolov10可用

这些标签是用于实例分割任务中的类别&#xff0c;通常在汽车图像识别或自动驾驶技术中使用。以下是这些类别&#xff1a; back_bumper - 后保险杠back_glass - 后挡风玻璃back_left_door - 后左车门back_left_light - 后左灯back_right_door - 后右车门back_right_light - 后右…

Linux进程-2

一&#xff1a;进程优先级 基本概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用&#xff0c;可以改善系统性能。 还可以把进程运行到指定的CPU上&#…

9_28代码实现登录框 注释

注释&#xff1a; .h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> // 防止头文件重复包含QT_BEGIN_NAMESPACE namespace Ui { class Widget; } //Ui表示命名空间的名称 //{ class Widget; } &#xff1a;表示在Ui命名空间中声明一个其他文件midget的类 QT_…

前段辅助工具分享(像素大厨)

引言&#xff1a; 我们在从事前端开发工作时&#xff0c;常会需要测量许多盒子的尺寸&#xff0c;颜色提取种种&#xff0c;切图&#xff0c;还有文字大小等信息&#xff0c;光从肉眼很难看出来&#xff0c;当然我们传统的会使用Photoshop来帮助我们完成这些工作&#xff0c;但…

LeetCode[中等] 17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 思路 回溯法 log&#xff1a;当前结果数组&#xff1b;level&#xff1a…

How to install JetBrains ToolBox in Ubuntu 22.04 LTS?

JetBrains Toolbox 的安装教程 在 2024 年 9 月 28 日&#xff0c;我想和大家分享一下 JetBrains Toolbox 的安装步骤&#xff0c;让你轻松开启高效的开发之旅。 一、准备工作 首先&#xff0c;确保你已经准备好了要安装的 JetBrains Toolbox 文件&#xff0c;可以从官方网站…

想入职网络安全?小白必看面试题(进阶版)(最终章)

大家好&#xff0c;我是Dest1ny&#xff01; 有同学问我为什么还要发面试题目。 因为我忘记发了。。。 这是最后一点面试题&#xff0c; 给大家发完&#xff01; Java基础 1. ★★☆☆☆ equals与的区别 比较引用是否相同&#xff0c;equals() 比较内容是否相同。 2. ★★…

MATLAB编写的RSSI在三维空间上的定位程序,锚点数量无限制(可自定义),带中文注释

文章目录 程序说明功能概述环境设置结果可视化函数定义运行结果程序获取 程序说明 功能概述 该程序实现了在三维空间中通过接收信号强度指示&#xff08;RSSI&#xff09;进行定位的功能。利用多个锚节点的信号强度测量&#xff0c;估计未知点的位置。该程序适用于无线定位应…

【深度学习】—线性回归 线性回归的基本元素 线性模型 损失函数 解析解 随机梯度下降

【深度学习】— 线性回归线性回归的基本元素 线性模型损失函数解析解随机梯度下降小批量随机梯度下降梯度下降算法的详细步骤解释公式 线性回归 回归&#xff08;regression&#xff09;是能为⼀个或多个⾃变量与因变量之间关系建模的⼀类⽅法。在⾃然科学和社会科学领域&…

【MYSQL】授权远程连接的用户

文章目录 一、Navicat新建查询1.点击新建查询&#xff0c;授权远程连接的用户语句&#xff1a;2.刷新权限 二、Navicat用户 一、Navicat新建查询 1.点击新建查询&#xff0c;授权远程连接的用户语句&#xff1a; GRANT ALL PRIVILEGES ON *.* TO 库的用户名授权IP IDENTIFIED …

注册器设计模式,以SparseInst(detectron2)代码为例

注册器设计模式 from detectron2.utils.registry import Registry 这里面的Registry实际上就是注册器设计模式 1.举一个简单版的小例子来理解注册器设计模式 参考&#xff1a;Python注册器设计模式_python 注册类-CSDN博客 # 这一行代码是从Python的 typing 模块中导入了一些…