当前位置: 首页 > news >正文

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

文章目录

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

一. 拦截器

1. 什么是拦截器

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

2. 拦截器的使用

在这里插入图片描述

自定义拦截器

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

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("目标方法执行前执行");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("目标方法执行后执行");}@Overridepublic 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;@Overridepublic 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. 注入拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;//2. 重写方法, 配置拦截器@Overridepublic 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;}@Overridepublic 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 {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic 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 {@ExceptionHandlerpublic Object handler(Exception e){return Result.fail(e.getMessage());}
}

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

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

相关文章:

  • QML使用Qt自带软键盘例子
  • Robot Operating System——带有协方差矩阵的三维空间中的位姿(位置和方向)
  • 成都睿明智科技有限公司赋能商家高效变现
  • OpenHarmony(鸿蒙南向)——平台驱动开发【PIN】
  • N诺计算机考研-错题(DS)
  • Qt C++设计模式->享元模式
  • [C++][IO流][流输入输出][截断理解]详细讲解
  • 【EXCEL数据处理】000011 案列 EXCEL带有三角形图标的单元格转换
  • OSPF路由计算
  • golang rpc
  • 若依从redis中获取用户列表
  • Java使用Redis的详细教程
  • AIGC: 10 AI转文服务器的搭建过程记录
  • CTMO时代下的营销新力量:2+1链动模式AI智能名片商城小程序
  • 【React】react项目中的redux使用
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • JavaScript创建对象的四种方式
  • Java的Interrupt与线程中断
  • Mysql优化
  • nodejs调试方法
  • Objective-C 中关联引用的概念
  • ReactNative开发常用的三方模块
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 类orAPI - 收藏集 - 掘金
  • 聊聊sentinel的DegradeSlot
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • zabbix3.2监控linux磁盘IO
  • 带你开发类似Pokemon Go的AR游戏
  • # 飞书APP集成平台-数字化落地
  • #、%和$符号在OGNL表达式中经常出现
  • #数据结构 笔记三
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (day18) leetcode 204.计数质数
  • (ISPRS,2021)具有遥感知识图谱的鲁棒深度对齐网络用于零样本和广义零样本遥感图像场景分类
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (PySpark)RDD实验实战——求商品销量排行
  • (Python第六天)文件处理
  • (独孤九剑)--文件系统
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (力扣)循环队列的实现与详解(C语言)
  • (算法)前K大的和
  • (五)activiti-modeler 编辑器初步优化
  • (一) storm的集群安装与配置
  • (转)Oracle 9i 数据库设计指引全集(1)
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • ***利用Ms05002溢出找“肉鸡
  • .net 4.0发布后不能正常显示图片问题
  • .NET Core 发展历程和版本迭代
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .net 设置默认首页