SpringBoot 统一功能处理之拦截器、数据返回格式、异常处理

news/2025/3/15 23:25:06/

目录

拦截器

一、什么是拦截器

 二 拦截器的使用

 三 拦截路径配置

四 拦截器的执行流程

统一数据返回格式

统一异常处理 


拦截器

一、什么是拦截器

        拦截器是Spring框架提供的核心功能之一,主要用来拦截用户的请求,在指定方法前后,根据业务需要执行预先设定的代码

        也就是说,允许开发人员提前预定于一些逻辑,在用户的请求响应前后执行,也可以在用户请求前阻止其执行

比如通过拦截器来拦截前端发来的请求,判断Session中是否有登录用户的信息,有的话正常响应,没有则进行拦截

 二 拦截器的使用

        拦截器的使用步骤分为两步:

  1.         定义拦截器
  2.         注册配置拦截器

1️⃣、首先自定义一个拦截器类(LoginInterceptor) 并实现HandlerInterceptor接⼝,并重写其所有⽅法 (这里我只重新了preHandle方法,根据自己的需求来决定的)

java">// 使用slf4j日志框架记录日志
// 将该类标记为Spring组件,使其被自动扫描并注册到Spring容器中
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {// 实现preHandle方法,该方法将在请求处理之前被调用// 其主要作用是进行登录验证,确保请求是经过认证的@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1. 获取token// 从HTTP请求的头部信息中获取token,这是进行登录验证的关键步骤//2、校验token 判断是否放行// 通过日志记录token信息,便于调试和排查问题log.info("进入拦截器");String token = request.getHeader(Constants.REQUEST_HEADER_TOKEN);log.info("neader中获取token:{}", token);// 这里使用JwtUtils工具类来解析token,以验证其有效性Claims claims=JwtUtils.parseToken(token);// 如果token解析失败,设置HTTP响应状态码为401,表示未授权,并阻止请求继续执行if(claims == null){response.setStatus(401);return false;}// 如果token解析成功,表示验证通过,允许请求继续执行return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("LoginInterceptor 目标方法执行后执行");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("LoginInterceptor 视图渲染完毕后执⾏");}
}
  • preHandle()⽅法:⽬标⽅法执⾏前执⾏. 返回true: 继续执⾏后续操作; 返回false: 中断后续操作.
  • postHandle()⽅法:⽬标⽅法执⾏后执⾏
  • afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图, 暂不了解)

2️⃣注册配置拦截器 : 实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法

java">//五大注解 其中的一个  将该类标记为Spring组件,使其被自动扫描并注册到Spring容器中
@Configuration
public class WebConfig implements WebMvcConfigurer {//注入自定义拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//addInterceptor是注册自定义拦截器//addPathPatterns是要添加拦截路径registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); //表示拦截所以请求}
}

 启动服务, 试试访问任意请求, 观察后端⽇志

可以看到preHandle ⽅法执⾏之后就放⾏了, 开始执⾏⽬标⽅法, ⽬标⽅法执⾏完成之后执⾏
postHandle和afterCompletion⽅法

我们把拦截器中preHandle⽅法的返回值改为false, 再观察运⾏结果 

可以看到, 拦截器拦截了请求, 没有进⾏响应.

 三 拦截路径配置

 拦截路径是指我们定义的拦截器 对那些请求生效

  • 通过 addPathPatterns() ⽅法指定要拦截哪些请求.
  • 通过 excludePathPatterns() 指定不拦截哪些请求
java">/*** Web配置类,实现WebMvcConfigurer接口以自定义Spring MVC的配置*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;/*** 需要排除拦截的路径列表,这些路径主要用于静态资源和编辑器的访问*/private final List excludes =  Arrays.asList("/**/*.html","/blog-editormd/**","/css/**","/js/**","/pic/**","/user/login");/*** 添加拦截器配置** @param registry InterceptorRegistry对象,用于注册自定义拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(excludes);//排除前端静态资源}
}

以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件, JS 和 CSS 等⽂件)

 

四 拦截器的执行流程

        正常调用流程 如下图:

 

配置拦截器后的调用流程 如下图:

 

  1. 添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法, 这个⽅法需要返回⼀个布尔类型的值. 如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的 ⽅法. 如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).
  2.  controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个方法afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据

统一数据返回格式

统一的数据返回格式使用 @ControllerAdvice 注解和 ResponseBodyAdvice 的方式实现。ControllerAdvice 表示控制器通知类。

java">/*** 全局响应建议类,用于统一处理控制器的响应体* 实现了ResponseBodyAdvice接口以定制响应体的处理*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@AutowiredObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 如果已经是Result类型,则不作处理,直接返回if(body instanceof Result){return body;}// 如果是字符串类型,序列化封装后的成功结果为JSON字符串if(body   instanceof  String){objectMapper.writeValueAsString(Result.success(body));}// 对于其他类型,直接封装为成功结果并返回return Result.success(body);}
}

继承 ResponseBodyAdvice 接口后,需要实现该接口下的 supports 方法和 beforeBodyWrite 方法,supports 方法只需要更改返回值为 true 就可以了,表示是否要执行 beforeBodyWrite 方法,返回 true 表示执行,false 表示不执行,beforeBodyWrite 方法中的 body 参数就是我们原方法的返回值。

注意@SneakyThrows 注解 主要目的是解决 Java 的异常处理问题。当我们在代码中抛出一个异常时,如果这个异常被包裹在一个方法中,并且这个方法没有 throws 关键字来声明会抛出这个异常,那么编译器会报错。通过使用 @SneakyThrows,你可以告诉编译器:“我知道这个方法可能会抛出异常,但我保证在 catch 块中处理它。” 这样编译器就不会报错了

定义一个统一的返回类型 :

java">
@Data
public class Result<T> {private ResultStatus code;//错误信息private String errMsg;//接口响应的数据private T data;public static <T> Result<T> success(T data){Result result = new Result();result.setCode(ResultStatus.SUCCESS);result.setErrMsg("");result.setData(data);return result;}public static <T> Result<T> fail(String errMsg){Result result = new Result();result.setCode(ResultStatus.FAIL);result.setErrMsg(errMsg);result.setData(null);return result;}public static <T> Result<T> fail(String errMsg, T data){Result result = new Result();result.setCode(ResultStatus.FAIL);result.setErrMsg(errMsg);result.setData(data);return result;}
}

统一数据返回格式的优点:

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

统一异常处理 

@ControllerAdvice 表⽰控制器通知类
@ExceptionHandler 是异常处理器
两个结合表 ⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件
具体代码如下:
java">@Slf4j
@ResponseBody //因为返回的数据都不是视图类型,所以加上这个注解防止出现问题
@RestControllerAdvice
public class ExceptionHandle extends RuntimeException{@ExceptionHandlerpublic Result Handle(NullPointerException e){log.error("空指针异常 ",e);return Result.fail("内部错误,请联系管理员");}@ExceptionHandlerpublic Result Handle(Exception e){log.error("发生异常 ",e);return Result.fail("内部错误,请联系管理员");}@ExceptionHandlerpublic Result Handle(NoSuchFieldException e){log.error("文件不存在:{}",e.getMessage());return Result.fail("内部错误,请联系管理员");}@ExceptionHandlerpublic Result Handle(RuntimeException e){log.error("运行时错误 ",e.getMessage());return  Result.fail("内部错误,请联系管理员");}}
类名, ⽅法名和返回值可以⾃定义, 重要的是注解
接⼝返回为数据时, 需要加 @ResponseBody 注解

例子 如图: 

 

 


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

相关文章

【数据结构初阶第十节】队列(详解+附源码)

好久不见。。。别不开心了&#xff0c;听听喜欢的歌吧 必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 目录 一、概念和结构 二、队列的实现 Queue.h Queue.c test.c Relaxing Time&#xff01; ————————————《有没…

计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

2025 年前端开发现状分析:卷疯了还是卷麻了?

一、前端现状&#xff1a;框架狂飙&#xff0c;开发者崩溃 如果你是个前端开发者&#xff0c;那么你大概率经历过这些场景&#xff1a; 早上打开 CSDN&#xff08;或者掘金&#xff0c;随便&#xff09;&#xff0c;发现又有新框架发布了&#xff0c;名字可能是 VueXNext.js 之…

腿足机器人之二- 运动控制概览

腿足机器人之二运动控制概览 高层运动规划MPCRL 中层逆运动学和逆动力学底层执行器控制传感器校正 上一篇博客是腿足机器人的骨架和关节的机械和电气组件&#xff0c;关节不仅需要通过机械设计实现复杂的运动能力&#xff0c;还必须通过电子组件和控制系统来精确控制这些运动。…

Docker的前世今生及安装与使用命令详解

在云原生时代&#xff0c;容器技术已经成为软件开发与部署的关键工具。其中&#xff0c;Docker 凭借其轻量、灵活和高效的特性迅速走红。本文将带你走进 Docker 的历史沿革&#xff0c;了解其从前世到今生的发展历程&#xff0c;并详细介绍如何安装 Docker 以及常用的操作命令。…

Django 创建表时 “__str__ ”方法的使用

在 Django 模型中&#xff0c;__str__ 方法是一个 Python 特殊方法&#xff08;也称为“魔术方法”&#xff09;&#xff0c;用于定义对象的字符串表示形式。它的作用是控制当对象被转换为字符串时&#xff0c;应该返回什么样的内容。 示例&#xff1a; 我在初学ModelForm时尝…

Ollama+DeepSeek+Open-WebUi

环境准备 Docker Ollama Open-WebUi Ollama 下载地址&#xff1a;Ollama docker安装ollama docker run -d \ -v /data/ollama/data:/root/.ollama \ -p 11434:11434 \ --name ollama ollama/ollama 下载模型 Ollama模型仓库 # 示例&#xff1a;安装deepseek-r1:7b doc…

SpringMVC重定向接口,参数暴露在url中解决方案!RedirectAttributes

OK&#xff0c;首先描述下业务场景&#xff0c;终端数量限制登录 1.首先访问项目login的get接口 2.输入账号密码点击登录后&#xff0c;会请求login的POST接口 3.后台对终端数量逻辑处理不允许登录跳回到登录页面 4.因代码原因需在后台进行多次重定向接口&#xff0c;最后跳…