SpringBoot统一功能处理(AOP思想实现)(统一用户登录权限验证 / 异常处理 / 数据格式返回)

news2024/11/11 6:53:43

主要是三个处理:

        1、统一用户登录权限验证;

        2、统一异常处理;

        3、统一数据格式返回。

目录

一、用户登录权限校验

🍅  1、使用拦截器

        🎈 1.1自定义拦截器

        🎈 1.2 设置自定义拦截器

        🎈创建controller类,并且运行项目

 🍅 2、拦截器原理

二、统一异常处理

三、统一数据返回

 🍅 为什么需要统一数据返回格式

🍅 统一数据返回格式

       🎈定义同已返回类型

       🎈 同以数据处理

        🎈业务类


一、用户登录权限校验

🍅  1、使用拦截器

可以对一部分方法进行拦截,而另一部分不拦截。

        🎈 1.1自定义拦截器

/*
* 全局变量
* */
public class AppVar {
//    Session key
    public static final String SESSION_KEY = "SESSION KEY";
}

/*
* 自定义拦截器
* 返回true -> 表示拦截器验证成功,继续指定后续方法
* 返回false -> 表示验证失败,不会执行后续的方法了
* */
@Component
public class UserInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
//        业务方法
        HttpSession session = request.getSession(false);
        if (session!=null &&
                session.getAttribute(AppVar.SESSION_KEY)!=null){
//            用户已经登录
            return true;
        }
        return false;
    }
}

        🎈 1.2 设置自定义拦截器

将自定义拦截器设置当前项目的配置文件中,并且设置拦截规则。

拦截器要注入到Spring中才能运行,他是伴随着Spring的启动而启动的

@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Autowired
    private UserInterceptor userInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(new UserInterceptor());
        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**")    //("/**")表示拦截所有的请求
                .excludePathPatterns("/user/reg")//表示过滤拦截,不拦截(/user/reg)
                .excludePathPatterns("/user/login");//表示过滤拦截,不拦截("/user/login")
    }
}

        🎈创建controller类,并且运行项目


@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public String getUser(){
        System.out.println("do getUser()");
        return "user";
    }

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        return "reg";
    }

    @RequestMapping("/login")
    public String login(){
        System.out.println("do login()");
        return "login";
    }
}

其中:

  • addPathPatterns:表示要拦截的url,“/**”表示拦截任意方法
  • elcludePathPatterns:表示需要排除的URL
  • 以上得拦截规则可以拦截URL,包括静态文件(图片文件、JS、CSS等),一般拦截静态文件的时候,我们可以把这些静态文件分类放在static文件中

 🍅 2、拦截器原理

在使用拦截器之前

 

 

使用拦截器之后:会在调用Controller之前进行相应的业务处理

实现原理源码分析:

          所有的controller指定都会通过一个调度器DispatcherServlet来实现,

 

所有的请求都会执行DispatcherServlet中的doDispatcher方法,在doDispatcher会执行一系列的事件,该事件是在执行拦截器之前的,如果该事件返回false,后续就不会执行Controller。

以下是doDispatcher中的一部分代码,发现在执行controller之前都会追先执行预处理

// 调⽤预处理【重点】

 if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {
 return;
 }
 // 执⾏ Controller 中的业务

 mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
}

那么,关于预处理⽅法 applyPreHandle方法:从上面的源码可以看出,着和我们之前定义的拦截器相似,着就是拦截器的实现原理

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex

                = i++) {
            // 获取项⽬中使⽤的拦截器 HandlerInterceptor

            HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
            torList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null

                );
                return false;
            }
        }
        return true;
    }

二、统一异常处理

统一异常处理:就是指常规的异常,统一处理。

统一异常处理使用的是@ControllerAdvice + @ExceptionHandler来执行的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

首先认为构造一个空指针异常:

 @RequestMapping("/reg")
    public String reg(){
        System.out.println("do reg()");
        Object obj = null;
        System.out.println(obj.hashCode());
        System.out.println();
        System.out.println("do reg()");
        return "reg";
    }

报错了: 这种直接给你报错的方式并不直观,所以我们可以进行统一的异常处理,返回直观的数据。

 然后我们进行统一异常处理:

        首先定义一个统一的返回对象:

@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据
}

        然后定义异常管理器:

@RestControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(NullPointerException.class)
    public ResultAjax doNullPointerException(NullPointerException e){
        ResultAjax resultAjax = new ResultAjax();
        //错误的信息使用-1描述状态码
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }
}

这时候就会返回状态的描述信息:


 也可以直接使用NullPointerException的父类

@ExceptionHandler(Exception.class)
    public ResultAjax doException(Exception e){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(-1);
        resultAjax.setMsg("空指针异常:"+ e.getMessage());
        resultAjax.setData(null);
        return resultAjax;
    }

三、统一数据返回

 🍅 为什么需要统一数据返回格式

统一数据返回格式的优点(为什么要统一):

  • 方便程序员更好的接收和解析后端数据接口返回的数据
  • 降低前端程序源和后端程序员的沟通成本,按照找某个格式实现,所有接口都这样返回
  • 有利于项目统一数据的维护和修改
  • 有利于后端技术部分的统一规范的标准制定,不会出现稀奇古怪的返回内容

🍅 统一数据返回格式

       🎈定义同已返回类型

@Data
public class ResultAjax {
    private int code;   //状态码
    private String msg; //状态码的描述信息
    private Object data;//返回数据

    /*
    * 返回成功
    * */
    public static ResultAjax success(Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(200);
        resultAjax.setMsg("");
        resultAjax.setData(data);
        return resultAjax;
    }


    /*
    * 返回失败
    * */
    public static ResultAjax fail(int code,String msg){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }

    public static ResultAjax fail(int code,String msg,Object data){
        ResultAjax resultAjax = new ResultAjax();
        resultAjax.setCode(code);
        resultAjax.setMsg(msg);
        resultAjax.setData(null);
        return resultAjax;
    }
}

              🎈 同以数据处理

统一数据处理(强制执行):

  1. @ControllerAdvice
  2. 实现ResponseBodyAdvice接口,并且重写它其中的两个方法,supports必须返回true,beforeBodyWrite方法进行重新判断和重写操作
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /*
    * 默认翻会true的时候
    * 才会执行beforeBodyWrite方法
    * */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        没有包装
        return ResultAjax.success(body);

    }
}

        🎈业务类

@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
    public ResultAjax login(){
        System.out.println("do login()");
        return ResultAjax.success("login");
    }
    
    @RequestMapping("/getnum")
    public int getNum(){
        return 1;
    }
}

其中login没有定义返回类型,getNum定义了返回类型,返回结果分别如下:

 

注意:

        如果定义的返回值类型是String,那么会报错

  @RequestMapping("/getstring")
    public String getString(){
        return "qqq";
    }

那么可以对String类型作出单独处理:

@Autowired
    private ObjectMapper objectMapper;

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //已经包装好的对象
        if (body instanceof ResultAjax){
            return body;
        }
//        对字符串进行单独处理
        if (body instanceof String){
            ResultAjax resultAjax = ResultAjax.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
//        没有包装
        return ResultAjax.success(body);
    }

返回结果:

 

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

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

相关文章

vue3中使用原始标签制作一个拖拽和点击上传组件上传成功后展示

在Vue3中&#xff0c;可以使用<input type"file">标签来实现上传文件的功能&#xff0c;同时可以通过<div>标签来实现拖拽上传的功能。 首先&#xff0c;在template中定义一个包含<input>和<div>标签的组件&#xff1a; <template>&…

使用Vscode编辑keil工程

一、需要安装的插件 1. Keil Assistant 2. C/C 3. 中文配置&#xff1a; 二、插件配置 1. Keil Assistant 添加Keil的安装路径 接下来就可以使用vscode编辑Keil的工程了&#xff0c;调试编译和下载程序需要返回到Keil中进行操作。 三、Vscode常用快捷键 可以自定义进行配置…

【Unity实用插件篇】| 学会使用 可编程瓦片Tile Map,快速搭建2D地图

前言【Unity 实用插件篇】| 学会使用 可编程瓦片Tile Map,快速搭建2D地图一、导入 Tile Map Editor二、创建调色板 Tile Palette三、快速绘制地图四、TilePalette 调色板功能介绍五、TileMap 相关组件属性介绍GirdTilemapTilemap Renderer 瓦片地图渲染器Tile Assets 瓦片资源…

如何创建百度百科词条?教你如何在百度百科上创建个人专业词条

百度百科是一个拥有海量内容的知识库&#xff0c;每天有数百万人访问该网站以获取有用的信息和知识。如果您是一家企业、一位专业人士或学者&#xff0c;那么在百度百科上创建一个词条是一个很好的品牌推广或个人形象塑造的机会。在本文中&#xff0c;一秒推小编将讲述如何创建…

安防视频综合管理合平台EasyCVR可支持的视频播放协议有哪些?

EasyDarwin开源流媒体视频EasyCVR安防监控平台可提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、云台控制、语音对讲、智能分析等能力。 视频监控综合管理平台EasyCVR具备视频融合能力&#xff0c;平台基于云边端一体化架构&#xff0c;具有强大的…

JVM基础篇-方法区与运行时常量池

JVM基础篇-方法区与运行时常量池 方法区 Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区。方法区类似于传统语言的编译代码的存储区或者类似于操作系统进程中的“文本”段。它存储每个类的结构&#xff0c;例如运行时常量池、字段和方法数据&#xff0c;以及方法和…

图像处理库(Opencv, Matplotlib, PIL)以及三者之间的转换

文章目录 1. Opencv2. Matplotlib3. PIL4. 三者的区别和相互转换5. Torchvision 中的相关转换库5.1 ToPILImage([mode])5.2 ToTensor5.3 PILToTensor 1. Opencv opencv的基本图像类型可以和numpy数组相互转化&#xff0c;因此可以直接调用torch.from_numpy(img) 将图像转换成t…

Sql server 2005 卸载之后重新安装

Sql server 2005 卸载之后重新安装 Sql sercer 2005在重新安装之前先要进行卸载操作&#xff0c;由于Sql sercer 2005组件都是分散的&#xff0c;所以卸载时要找到对应的位置一个一个卸载&#xff0c;不卸载干净的情况下再次安装时会出现很多问题&#xff0c;导致安装失败。这…

KubeSphere部署芋道源码ruoyi-vue-pro

KubeSphere环境准备 新建企业空间 新建项目 创建harbor镜像服务信息 新建DevOps 项目 创建git,harbor,kubeconfig凭证 中间件部署 MySQL8部署redis6部署 后端部署 修改pom.xml <!-- pom.xml添加内容&#xff1a;--> <profiles><!-- 本地环境 --><pro…

《当今奇人周兴和》励志小说连载之三 ● 饥饿寒冷童年梦

饥饿寒冷童年梦 Hungry and cold childhood 兴和至今也记得&#xff0c;这年冬天特别冷。 Xinghe still remembers that the winter was particularly cold in that year. 那一天&#xff0c;放了学的小兴和背着书兜&#xff0c;又冷又饿地从毛公乡小学出来&#xff0c;艰难…

渠道控价的常见方法

品牌渠道一般分为线上和线下&#xff0c;现在常见要治理的渠道一般指的是线上&#xff0c;因为线上渠道数据变化快&#xff0c;价格波动也很大&#xff0c;促销信息有很多&#xff0c;如果遇到大促&#xff0c;涉及的价格变化将会更明显&#xff0c;所以线上渠道是非常值得控价…

OA办公自动化系统设计与实现(论文+源码)_kaic

摘要 随着信息化建设的日益深入&#xff0c;无论是政府还是企事业单位&#xff0c;部门之间的信息沟通与协调工作越来越重要。人们迫切需要一个能充分利用网络优势&#xff0c;并可以管理企业的各种重要信息的软件平台&#xff0c;利用该平台快速建立自己的信息网络和办公管理系…

华为PMS API client token auth failed

对接华为pms时出现问题&#xff0c;提示华为PMS API client token auth failed 主要是权限的问题&#xff0c;创建项目的时候选择N/A

u盘数据不知道什么时候全没了怎么恢复

案例&#xff1a;“你好&#xff0c;我发现我的U盘上的所有数据都不知何时消失了&#xff0c;我之前保存了许多重要的文件和照片&#xff0c;U盘数据不知道什么时候全没了怎么恢复&#xff1f;大神你知道么” 随着U盘的广泛应用&#xff0c;数据丢失问题也日益突出。很多人发现…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 4

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

chrome扩展在popup、background、content之间通信解决传输文件问题

文章目录 背景介绍案例介绍代码示例popup页面&#xff0c;上传文件页面popup页面&#xff0c;js上传代码&#xff0c;file文件转base64background监听消息&#xff0c;base64转file文件&#xff0c;axios上传 附-转base64后直接下载 背景介绍 示例扩展API版本MV2。 以弹…

Linux下安装配置Redis

文章目录 安装依赖库上传安装包并解压 启动默认启动指定配置启动开机自启 安装 依赖库 Redis是基于C语言编写的&#xff0c;因此首先需要安装Redis所需要的gcc依赖&#xff1a; yum install -y gcc tcl上传安装包并解压 将Redis安装包上传到服务器的任意目录&#xff0c;例…

使用WebMvcConfigurationSupport后导致原来返回的json数据变为了xml的解决方法

问题 未使用WebMvcConfigurationSupport拦截时返回的数据都是JSON格式&#xff0c;使用WebMvcConfigurationSupport做拦截后数据的返回变为了XML的格式。 原因 在Spring框架中&#xff0c;WebMvcConfigurationSupport 是一个类&#xff0c;它可以用于自定义Spring MVC的配置…

【word密码】如何解密word密码?

Word文档常见的两种加密方式&#xff0c;打开密码和限制编辑&#xff0c;这两种密码对word文档加密&#xff0c;今天我们来讲一下这三种加密如何解密 解密一&#xff1a;打开密码 设置了打开密码的word文件&#xff0c;想要解密&#xff0c;我们需要先输入密码进入到文件当中…

IO进程线程第四天(8.1)opendir,closedir,readdir

作业1&#xff1a; 从终端获取一个文件的路径以及名字。 若该文件是目录文件&#xff0c;则将该文件下的所有文件的属性显示到终端&#xff0c;类似ls -l该文件夹 若该文件不是目录文件&#xff0c;则显示该文件的属性到终端上&#xff0c;类似ls -l这单个文件 #include<…