Spring Boot 拦截器实现:登录验证 统一异常处理 返回数据规范化

news2024/11/28 22:56:06


        学习 Spring 和 servlet 初期,我们在判断用户身份时,都是在每个方法中获取会话、获取对象,这种方式冗余度高,增加代码复杂度,维护成本也高,因此想到可以使用 AOP 来实现一个公共的方法,这个公共的方法专门做判断用户身份,但 AOP 的通知方法是整个横切面都会其进行拦截的,所以对于有的不需要判断用户身份的方法而言,这样处理太过暴力,也不合法,除此之外,使用 AOP 也在获取获取参数上也较为困难,因此使用 Spring Boot 拦截器来实现更加合适。

        下面使用拦截器分别实现(1) 统一登录验证、(2)统一异常处理、(3)统一返回数据格式,这三个实战性的功能。


1. 统一登录验证:

第一步:自定义拦截器

        创建一个普通类,实现 HandlerInterceptor 接口,重写 preHandle 方法,由于拦截器非常常用,所以Spring boot 内置了拦截器的依赖;

  1. preHandle方法:在请求到达Controller之前调用。在拦截器链中的每个拦截器的preHandle方法都会被依次调用。如果某个拦截器的preHandle方法返回false,则后续的拦截器的preHandle方法和Controller方法都不会被执行,请求将被拦截下来。

  2. postHandle方法:在Controller方法执行后,DispatcherServlet渲染视图之前调用。在拦截器链中的每个拦截器的postHandle方法都会被依次调用。可以在这个方法中对ModelAndView进行处理或者添加公共的模型数据。

  3. afterCompletion方法:在DispatcherServlet完成视图渲染之后调用。在拦截器链中的每个拦截器的afterCompletion方法都会被依次调用。可以在这个方法中进行一些资源清理操作,比如释放资源、记录日志等。该方法在整个请求处理流程结束之后被调用。

第二步:注册拦截配置

        写一个普通方法,实现WebMvcConfigurer接口,重写addInterceptor方法:目的是将自定义拦截器配置到系统配置项里,并设置合理的拦截路径。

第三步:添加 Controller 方法 

 执行效果:login不能进入,register可以进入


扩展:添加统一访问前缀:

        假设一种场景,当多个项目有同名的 url 时,在测试抓包等观察过程中如何分别它们各自属于哪个项目呢?

        给请求地址添加一个访问前缀,就可以实现区分了。

 加上前缀后还要记得更改拦截路径:


2. 统一异常处理:

        统一异常处理是指,某些方法可能会触发同一类异常,我们可以借助拦截器去统一拦截获取到,并根据约定返回需要的结果。

第一步:创建异常处理类和方法

        写一个普通类,它内部包含捕获所有异常类的方法,给处理类添加 @ControllerAdvice 注解,给自定义拦截方法添加@ExceptionHandler 注解,注解内传参要传要捕获的异常类对象:

 第二步:编写可能出现异常的方法

有异常检测时,如果出现对应异常,服务器依然按照异常处理方法里的规定给前端返回该返回的东西:

 当没有异常检测时,即使出现对应的异常,服务器的返回和约定无关:

2.1 父子异常:

        上面这种,注解@ExceptionHandler 参数中只有一种异常时,只能捕获处理对应这一种异常,如果想处理很多异常,不用每个都写一个方法,可以在参数中直接传所有异常的父类异常类如:Exception.class

        如果有子异常和父异常的处理方法同时存在时,就先匹配子异常检测方法


3. 统一数据格式返回:

        在日常开发中,后端每个业务给前端返回的数据格式要符合预定格式,但如果有的人忘记了约定格式、或者新人不知道预定格式,可能会按照自己的想法返回数据,这样就会导致业务事故。

        统一数据返回,使用 @ControllerAdvice 注解 + ResponseBodyAdvice 接口实现,我们可以在自定义类中设定好符合约定的返回数据格式,然后拦截每个业务返回的数据,判断它返回的数据格式是否符合约定规范,如果不符合就重新封装数据,返回拦截规则里的数据格式。

        强制性统一数据返回,在返回数据之前进行数据重写

第一步:创建数据处理类

        写一个普通类,实现  ResponseAdvice 接口,重写 supports() 和 beforeBodyWrite() 方法。

  • supports() 方法:该方法相当于一个开关,用来告诉处理方法是否要重写拦截到的内容,返回 true 表示要重写;
  • beforeBodyWrite() 方法:拦截到业务方法要返回的数据,在它返回自己数据前重写错误格式的数据;

 第二步:创建两个可能出现错误格式数据的方法

         getRet1() 方法返回一个 1,getRet2() 方法返回一个 hashMap
        约定正确的格式为一个 hashMap

 总结:

这种自定义拦截数据格式返回存在两个问题:

1.处理方法中设定的返回内容的值是固定写死的;

2.当返回String类型,不能被处理成正确数据;

3.1 扩展:解决转换 String 类型返回值出现错误的问题

        先演示一下,getRer3() 方法,返回的是字符串,理想状态应该是被拦截后被转换成 封装好的 hashMap进行返回,但是没有这样做,因为无法转换,所以出现类转换异常,被之前写的异常处理类捕获到返回异常: HashMap 不能转换为 String

错误原因:

        肯定有疑问:我明明是将 String 转换为 hashMap,为什么报错信息是反过来的?

这里要明白返回的执行流程:

  1. 原方法返回的原 body 是 String 类型;
  2. 统一数据返回之前:将 String 转换为 hashMap;
  3. 浏览器会借助 StringHttpMessageConverter 将 hashMap 转换为 json 字符串;

问题就出在了第三步,浏览器判断时用原body判断是不是 String 类型,是的话就用  StringHttpMessageConverter 将 hashMap 转换为 json,发现不能转换,所以报错。

解决方案1:

在统一数据重写时,单独处理返回值为 String 类型的情况,重写成返回一个 Json 字符串(可以拼接、可以使用jackson),而非 HashMap;

 使用 Jackson 转成 String:

解决方案2:

        既然问题出在了浏览器的转换器上,那我们就想办法不用这个转换器了;

        可以手动将 StringHttpMessageConverter 转换器去掉:

 这样就好了:


4. 拦截器的实现原理:

 图解:

        所有的 Controller 执行都会通过⼀个调度器 DispatcherServlet 来实现

上图解释了拦截器的实现原理:

  1. 请求到达DispatcherServlet,DispatcherServlet是前端控制器,负责接收请求并进行分发。

  2. DispatcherServlet根据配置的拦截器链,依次调用每个拦截器的方法。

  3. 拦截器链中的每个拦截器都实现了 HandlerInterceptor 接口,拦截器的方法会在请求处理的不同阶段被调用。

  4. 在调用每个拦截器的方法之前和之后,会根据返回值来决定是否继续执行下一个拦截器或者Controller方法。

  5. 当所有拦截器的方法都执行完毕后,DispatcherServlet会进行视图渲染,生成响应结果。

通过拦截器的实现,可以在请求处理的不同阶段进行拦截和处理,实现一些通用的功能,比如权限验证、日志记录、异常处理等。拦截器的实现原理基于Java的反射机制和设计模式,通过动态代理生成代理对象,来实现拦截器的调用。

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

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

相关文章

Android 自定义跳转到系统 Settings Fragment 的 Intent

以跳转到蓝牙控制面板为例,控制面板如图所示: 其 Fragment 所在的位置是: packages/apps/Settings/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java 第一步 要在 Settings的主要 Activity 中定义继承同一个父类…

通达信波段王指标公式_通达信公式

使用说明:1、买点:多空分界线变红后出现波段买为做多信号,中间出现波段卖信号为杂波可以不考虑,再结合逃顶信号进行卖出操作,如果没有出现逃顶信号,则可以等多空分界线变绿后结合波段卖信号综合做出判断。2…

基于proteus的纯模拟病房呼叫系统

摘要:无线的病房呼叫系统将病人的呼叫请求迅速传递给医护者,减少了信息传递的过程性,病人无需等待医护巡查才可求助,增加了便捷性与即时性;此外也减轻一定的工作量,加强服务的效率。本文基于proteus软件进行…

selenium 启动常用浏览器驱动方式

一.启动 Chrome 浏览器 方式一: // 驱动路径的 File File file new File("src/main/resources/drivers/chromedriver-win32.exe"); // 设置系统属性,setProperty() 中两个参数分别是驱动名和 file 绝对路径 System.setProperty("webdr…

如何在Mkdocs里自定义字体(霞鹜文楷)

网站目前在用的字体:霞鹜文楷 想必你可以直观的从我的网站,感受到这款字体的美观程度。 以下是摘录的部分字体官方介绍文档 注意事项 添字请在 Issue #33 反馈,字形调整请在 Issue #14 反馈,不要另开议题,以便于整理。…

SpringBoot——持久化技术

简单介绍 在之前我们使用的数据层持久化技术使用的是MyBatis或者是MyBatis-plus,其实都是一样的。在使用之前,我们要导入对应的坐标,然后配置MyBatis特有的配置,比如说Mapper接口,或者XML配置文件,那么除了…

为什么pmp证书只能对标cspm二级证书??

CSPM 认证是新出台的证书,9月才开始第一次考试,前期肯定不会太难的,而 CSPM 有五个等级,综合下来 PMP 对标的是PMP是CSPM-2 级,难度差不多,PgMP对标的是CSPM-3。 CSPM 能力等级划分为五个等级,从…

得物词分发平台技术架构建设与演进

前言 在文章开始前先介绍下导购,导购通常是指帮助消费者在购物过程中做出最佳决策的人或系统。在电商网站中,导购可以引导用户关注热卖商品或促销活动等,帮助用户更好地进行购物。导购的目的是为了提高用户的购物体验,促进销售额…

通过认识在职研究生深入了解人大与加拿大女王大学中外合作办学金融硕士

随着社会经济的不断发展,职场竞争也愈发激烈、工作要求不断提高,许多从业人员既不想放弃工作,又想读研提升专业能力,深化对专业知识的理解,获取优质的证书。那么报考在职研究生就是一个非常不错的方式。若你选择人大与…

echarts图表如何自定义鼠标悬浮样式

样式调整 // formatter: {a}: {b}<br />{c}: {d}: {e} formatter: function (params) {// <i style"display:inline-block;border-radius:50%;width:4px;height:4px;background-color: #0ECB81;"></i>// ${console.log(params[0])}return <d…

Java版知识付费源码 Spring Cloud+Spring Boot 前后端分离实现知识付费平台

提供职业教育、企业培训、知识付费系统搭建服务。系统功能包含&#xff1a;录播课、直播课、题库、营销、公司组织架构、员工入职培训等。 提供私有化部署&#xff0c;免费售后&#xff0c;专业技术指导&#xff0c;支持PC、APP、H5、小程序多终端同步&#xff0c;支持二次开发…

SSL 证书过期巡检脚本

哈喽大家好&#xff0c;我是咸鱼 我们知道 SSL 证书是会过期的&#xff0c;一旦过期之后需要重新申请。如果没有及时更换证书的话&#xff0c;就有可能导致网站出问题&#xff0c;给公司业务带来一定的影响 所以说我们要每隔一定时间去检查网站上的 SSL 证书是否过期 如果公…

openlayers根据下拉框选项在地图上显示图标

这里是关于一个根据下拉框的选项在地图上显示图标的需求&#xff0c;用的是vueopenlayers 显示效果大概是这样&#xff1a; 选中选项之后会跳转到所点击的城市&#xff0c;并且在地图上显示图标温度&#xff0c;这一块UI没设计我就大概先弄了一下&#xff0c;比较丑。。 首先…

【JAVA】 String 方法附件

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 String 方法 String 方法 char charAt(int index)返回指定索引处的 char 值。int compareTo(Object o) 把这个字符串和另一个对象比较。 int compareTo(String anotherString)按…

如何才能做Cox呢?今天带来Cox回归分析步骤详解

Cox回归是生存分析中运用最多的一个模型&#xff0c;又称为比例风险回归模型(proportional hazards model)。是由英国统计学家D.R.Cox在1962年提出的&#xff0c;为了表示对他的尊敬&#xff0c;所以大家以他的名字命名这个模型。这是一种半参数回归模型&#xff0c;以结局和生…

linux | vscode | makefile | c++编译和调试

简单介绍环境&#xff1a; vscode 、centos、 gcc、g、makefile 简单来说就是&#xff0c;写好项目然后再自己写makefile脚本实现编译。所以看这篇博客的用户需要了解gcc编译的一些常用命令以及makefile语法。在网上看了很多教程&#xff0c;以及官网也看了很多次&#xff0c;最…

Kakfa 入门到起飞 - Kafka 遭大家诟病了?因为啥?啥是再平衡?

Kafka 遭大家诟病了&#xff1f;因为啥&#xff1f;啥是再平衡&#xff1f; 再均衡是Kafka被大家诟病最多的一个点&#xff0c;再平衡是非常麻烦的一个事&#xff0c;那么就让我们来看看 到底什么是再平衡呢&#xff1f; 再平衡其实就是一个 协议&#xff0c;它规定了消费者…

0成本搭建自己的云数据库

第一步&#xff0c;租免费的云服务器 www.aliyun.com 阿里云的&#xff0c;可以免费租三个月 进入主页后选择云服务器ESC 选择这款&#xff0c;点击试用就行 第二步&#xff0c;配置服务器 在配置服务器系统的时候选择centos&#xff0c;省事&#xff0c;别选ubuntu&#x…

Windows 不同方式打开的cmd/dos窗口属性配置不同

文章目录 1. 默认值&#xff08;控制台窗口&#xff09;属性2. "C:\Windows\System32\cmd.exe" 属性3. "命令提示符"属性4. 自定义某标题cmd窗口属性5. cmd快捷方式的属性总结 最近在写某个批处理脚本时&#xff0c;意外发现 Windows系统中&#xff0c;在不…

Is Mapping Necessary for Realistic PointGoal Navigation 论文阅读和代码分析

论文 论文信息 题目&#xff1a;Is Mapping Necessary for Realistic PointGoal Navigation? 作者&#xff1a;Ruslan Partsey、 Erik Wijmans 代码地址&#xff1a;rpartsey.github.io/pointgoalnav 来源&#xff1a;CVPR Abstract 目标&#xff1a;证明显式地图不是成功…