SpringBoot 统一功能处理

2023-11-20

目录

一. 统一用户登录权限验证

Spring 拦截器 

统一访问前缀添加

二. 统一异常处理

三. 统一数据格式返回

String 格式的特殊处理 


一. 统一用户登录权限验证

在没有统一功能处理之前,对于用户登录权限验证,每个方法都需要单独写用户登录验证的方法,就导致后期的修改和维护的成本大大提高 ,而这个验证方法与具体要实现的业务也一般没有关联,所以就需要提供一个公共的 AOP 方法来进行统一的用户登录权限验证。

Spring 拦截器 

此处就可以采用 Spring 拦截器 来处理问题,相比于 Spring AOP,它可以根据 HttpServletRequest 对象来获取到 HttpSession,而且对于一部分方法拦截,一部分方法不拦截的定义也更加方便。 

 1. 创建自定义拦截器,实现 HandleInterceptor 接口,实现 preHandle 方法,preHandle 方法表示执行具体方法之前的预处理,返回值为  boolean 类型,当返回 true 的时候,表示拦截器验证成功,继续走接下来的流程,执行目标方法,如果返回 false,则表示拦截器验证失败,验证未通过,后续的流程和目标方法就不再执行了;

//          自定义拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
//          调用目标方法之前执行的方法
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//          用户登录判断业务
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("session_userinfo")!=null){
//          用户已经登录,继续执行目标方法
            return true;
        }
//          验证失败,后续不再执行
        return false;
    }
}

2. 将自定义拦截器配置到系统配置项,并且设置合理的拦截规则。实现  WebMvcConfigurer 接口,实现方法 addInterceptors(InterceptorRegistry registry); 通过 registry.addInterceptor(对应的拦截器) 来将拦截器添加到系统配置项里,通过 registry.addInterceptor.addPathPatterns(拦截路径) 和 registry.addInterceptor.excludePathPatterns(放行路径) 来添加拦截规则


@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Autowired
    public LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        拦截器添加到系统配置项里面了    registry.addInterceptor(loginInterceptor)
        registry.addInterceptor(loginInterceptor)       // 设置拦截规则
                .addPathPatterns("/**")                 // /** 表示拦截所有路径(url)
                .excludePathPatterns("/**/login")        // 还要放行一些路径,比如登录和注册
                .excludePathPatterns("/user/reg");
    }
}

也可以拦截图片文件,JS文件等;

 "/**/*.jpg" "/**/*.js" "/**/*.css"

有了拦截器后的,执行流程就是:当用户调用的时候,就先进入拦截器进行预处理,判断是否满足业务条件,如果满足条件了,再进入控制器层进行数据交互和参数校验,然后进入服务层,再根据业务逻辑来决定要调用 哪个Mapper,从而访问数据库,再依次返回,而如果拦截器预处理不满足业务条件,那么后面也就不再需要执行了。 

 统一访问前缀添加

可以在访问的地址上添加前缀;

//    所有请求地址添加 add 前缀
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
//        其中第⼆个参数是⼀个表达式,设置为 true 表示启动前缀。
        configurer.addPathPrefix("add",c -> true);
    }
//    http://localhost:8080/add/user/login 加完前缀后要访问的 url 地址
//    excludePathPatterns() 中参数地址也要改为 add/user/reg

二. 统一异常处理

 统一异常处理使用:@ControllerAdvice + @ExceptionHandle 注解来实现。

1. @ControllerAdvice 表示控制器通知类,加这个注解之后,会去检测控制器的异常,如果控制器发生异常了,底下的方法就可以感知得到,感知到后就可以根据写的业务代码来将结果返回给前端;

2. @ExceptionHandle 是异常处理器,可以针对某个类型的异常进行监测和处理;

@ResponseBody + @ControllerAdvice 这两个注解可以用 @RestControllerAdvice 表示

@ControllerAdvice
@ResponseBody
public class MyExceptionAdvice {
//  检测异常类型
    @ExceptionHandler(NullPointerException.class)
    public HashMap<String,Object> doNullPointerException(NullPointerException e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",-1);
        result.put("msg","空指针:"+e.getMessage());
        result.put("data",null);
        return result;
    }
}

对于上述的代码就可以处理空指针异常,而异常的类型是有很多的,所以一般也就用父类 Exception 来作为处理。 当子类同时存在的时候,以子类的处理方法为主;

@ExceptionHandler(Exception.class)
    public HashMap<String,Object> doException(Exception e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",-1);
        result.put("msg","Exception: "+e.getMessage());
        result.put("data",null);
        return result;
}

三. 统一数据格式返回

 统一数据格式返回可以带来很多的方便:

1. 方便前端更好的接收和解析后端接口返回的数据;

2. 降低前后端之间的沟通成本;

3. 有利于项目后期的维护和修改;

主要使用注解:@ControllerAdvice + ResponseBodyAdvice 接口实现 ;

实现 ResponseBodyAdvice 接口的时候,需要重写 supports 方法 和 beforBodyWrite 方法,support 方法返回值 (布尔类型) 就决定于数据格式是否重写,数据重写是发生在给用户返回数据之前。beforeBodyWrite 方法就是重写数据格式,此方法中的参数 Object body 指的就是原始返回值;

对于统一数据格式返回,不是实现数据返回,可以理解为是为数据加工处理的一步,所以一般不加 @ResponseBody ;

在 beforeBodyWrite 方法中,主要关注参数 Object body, 指的是方法体的返回值,方法实现的时候就是针对这个值进行数据加工,让这个值以统一数据格式返回,

@ControllerAdvice
//@ResponseBody           // 他不是实现数据返回的,他可以理解为数据加工处理的一步,所以可以不加
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
//    数据的重写,执不执行取决于 supports 方法,true的时候才执行
//    给用户返回数据之前,进行数据重写
//    body 指的是原始返回值
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        主要关注 Object body,指的是方法体的返回值,对这个值进行数据加工
//        让这个数据以标准格式进行返回,此处假设标准格式为 hashMap<String,Object> -> code,msg,data
//        instanceof 判断当前类型
        if (body instanceof HashMap){
            return body;        // 满足 hashMap 的格式,直接返回
        }
//        不满足,重写返回结果,让其返回一个统一的数据格式      user/login 返回一个 int
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("data",body);    // 参数 body
        result.put("msg","");
        return result;
    }
}

String 格式的特殊处理 

但在统一数据格式返回的时候,如果遇到 String 类型的时候,会返回报错:大概意思是 HashMap 无法转换为 String 类型

此处可以对异常进行返回,也是因为前面的代码中做了统一异常处理;

对此我们作出解析,在返回的执行流程中,执行的顺序是:

1. 首先方法返回 String 类型:

 2. 统一数据返回之前处理:将 String 转为 HashMap 类型,也就是执行方法内容;

3. 最后将 HashMap 转换成 application / json 字符串给前端;

而这一步也正是报错对应的那一步:HashMap 无法转换为 String 类型

从原码的角度分析:

1. 当 body 的类型是 String 的时候,会使用 StringHttpMessageConverter 进行类型转换;而使用这个方法将 HashMap(对象) 转换为 String 是没办法的。

2. 当 body 的类型非 String 的时候, 会使用 HttpMessageConverter 进行类型转换; 

因此就有了两种解决方案:

1. 在配置中,将 StringHttpMessageConverter 去掉,那默认就会执行 HttpMessageConverter 方法,就不会出错:

// 当前的类作为系统配置项
// 解决方案一,去掉 StringHttpMessageConverter
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
    }
}

2. 在统一数据重写的时候,单独处理 String 类型,让对象转换成 String,让其返回一个 String 字符串,而非HashMap; (采用 Jackson 的方法)

@ControllerAdvice
//@ResponseBody           // 他不是实现数据返回的,他可以理解为数据加工处理的一步,所以可以不加
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
//    数据的重写,执不执行取决于 supports 方法,true的时候才执行
//    给用户返回数据之前,进行数据重写
//    body 指的是原始返回值
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        主要关注 Object body,指的是方法体的返回值,对这个值进行数据加工
//        让这个数据以标准格式进行返回,此处假设标准格式为 hashMap<String,Object> -> code,msg,data
//        instanceof 判断当前类型
        if (body instanceof HashMap){
            return body;        // 满足 hashMap 的格式,直接返回
        }

//        不满足,重写返回结果,让其返回一个统一的数据格式      user/login 返回一个 int
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("data",body);    // 参数 body
        result.put("msg","");

        //        解决特殊问题 String 类型,方案2
        if ( body instanceof String){
            try {
                return objectMapper.writeValueAsString(result);      // 将对象转换成字符串
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

 这样就可以解决问题了:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot 统一功能处理 的相关文章

随机推荐

  • AWS动手实验 - 创建一个Web3网站

    实验操作和录播 亚马逊云科技开发者社区 web3 dApp demo README CN md at main Chen188 web3 dApp demo GitHub 注意事项 按照操作手册进行即可 需要注意到的几个地方 1 EC2 的
  • C#使用Socket建立连接、通信,主动发送Close关闭, 随后进行下一次的连接,此时会出错,通信端口被占用

    C 使用Socket建立连接 通信之后 主动发送Close关闭 随后进行下一次的连接 此时会出错 通信端口被占用 当你关闭一个Socket连接后 操作系统会在一段时间内保持该端口处于TIME WAIT状态 在这个状态下 该端口是不可用的 直
  • Qt数据类型与强制转换(转)

    类型转换 把QString转换为 double类型 方法1 QString str 123 45 double val str toDouble val 123 45 方法2 很适合科学计数法形式转换 bool ok double d d
  • java源文件命名规则

    Java程序源文件的命名不是随意的 Java文件的命名必须满足如下规则 Java程序源文件的扩展名必须是 java 不能是其他文件扩展名 在通常情况下 Java程序源文件的主文件名可以是任意的 但有一种情况例外 如果Java程序源代码里定义
  • SpringMVC加载流程

    这节介绍SpringMVC SpringMVC是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架 本章会介绍相关概念 流程 再从源码进行讲解 1 MVC MVC Model View Controller 是一种软件设计
  • Zookeeper(三)—分布式锁实现

    一 独占锁原理 独占锁是利用zk同一目录下不能创建多个相同名称的节点这个特性 来实现分布式锁的功能 竞争锁的分布式系统 都在zk根目录下创建一个名为lock的节点 创建节点成功的系统 说明抢到了这把锁 没有创建成功的系统 说明这个节点已经被
  • 星星之火-22: 什么是手机小区重选?跳槽

    小区重选 cell reselection 指手机在空闲模式下 通过监测邻区和当前小区的信号质量以选择一个最好的小区提供服务信号的过程 选择了一家新公司 并不意味着永久待在一家公司 当前服务的公司 有可能由于经营状况变变糟 薪资水平下降 也
  • 【树莓派4B】darknet-nnpack的安装及使用

    文章目录 前言 步骤 1 下载依赖项 2 安装NNPACK darknet 3 下载darknet nnpack 4 使用YOLO进行预测 检测图像 检测视频 检测视频流 错误处理 make 时报错 undefined reference
  • (二)webpack-server

    宗旨 为了更好的开发和调试 1 package json npm init y 生成package json 2 安装server npm install webpack dev server D 3 修改配置 在package json文
  • canvas绘制一个圆分成六等分颜色随机

  • 基于FPGA的AHT10传感器温湿度读取

    文章目录 一 系统框架 二 i2c接口 三 i2c控制模块 状态机设计 状态转移图 START INIT CHECK INIT IDLE TRIGGER WAIT READ 代码 四 数据处理模块 串口 代码 五 仿真 testbench设
  • vue:实现锚点双向滚动/文章章节联动滚动效果

    文章目录 需求描述 实现思路 示例代码 参考网址 需求描述 需要实现类似doc中文档大纲的效果 点击对应章节的名称时定位到相应的正文 而当正文滚动时 高亮显示对应的章节名称 实现思路 其实笔者一开始想到的是利用a标签页内跳转 也就是 锚点
  • pandas学习笔记--增加行或列

    一 增加行 1 loc 想增加一行 行名称为 5 内容为 16 17 18 19 df loc 5 16 17 18 19 后面的序列是Iterable就行 2 at df at 5 16 17 18 19 3 set value df s
  • CTFShow web1-7——CTF秀WEB模块解题思路

    CTFShow WEB模块详细通关教程 受篇幅所限 通关教程分为上下两部分 第一部分为1 7关 第二部分为8 14关 本篇博客为1 7关的通关教程 从解题思路和原理剖析两个方面进行讲解 CTFShow web1 7关详细教程 解题思路 CT
  • 架构师必备技能之——MySQL数据库表设计

    好记忆不如烂笔头 能记下点东西 就记下点 有时间拿出来看看 也会发觉不一样的感受 目录 一 总体设计思想 二 字段相关设计原则 三 索引设计原则 四 SQL操作原则 五 其他原则 一 总体设计思想 1 不要在数据库做运算符操作 数据库服务器
  • FastJSON、Jackson、Gson性能测试

    起因是公司原先用的是阿里开源的FastJSON 大家用的也比较顺手 但是在出现了两次严重的漏洞后 公司决定放弃FastJSON 使用其他序列化 反序列化工具 考虑大家常用的无非就是FastJSON Jackson和Gson这三种 因此领导让
  • MyBatis 中如何使用多表查询

    MyBatis 中如何使用多表查询 MyBatis 是一款优秀的 ORM 框架 支持多表查询操作 在实际开发中 经常需要使用多表查询来获取业务数据 本文将介绍 MyBatis 中如何使用多表查询 包括使用嵌套查询 使用关联查询和使用动态 S
  • 六种进程间通信方式

    转载 六种进程间通信方式 LceChan的博客 CSDN博客 如何实现进程间通信
  • swiper 轮播 多行多列 横向排列

    一直没仔细研究过swiper 用到了swiper多行多列的展示效果 官网默认是纵向排列 想要做到横向排列 需添加一个一个属性 slidesPerColumnFill row slidesPerView 4 slidesPerColumn 4
  • SpringBoot 统一功能处理

    目录 一 统一用户登录权限验证 Spring 拦截器 统一访问前缀添加 二 统一异常处理 三 统一数据格式返回 String 格式的特殊处理 一 统一用户登录权限验证 在没有统一功能处理之前 对于用户登录权限验证 每个方法都需要单独写用户登