0
点赞
收藏
分享

微信扫一扫

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

微言记 2023-07-27 阅读 19


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

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


1. 统一登录验证:

第一步:自定义拦截器

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

第二步:注册拦截配置

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

第三步:添加 Controller 方法 

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


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

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

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

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


2. 统一异常处理:

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

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

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

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

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

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

2.1 父子异常:

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

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


3. 统一数据格式返回:

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

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

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

第一步:创建数据处理类

        写一个普通类,实现  ResponseAdvice 接口,重写 supports() 和 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 来实现

举报

相关推荐

0 条评论