Spring Boot统一异常处理 Spring拦截器

小编在前文中向大家描述了Spring AOP的相关内容:Spring AOP-CSDN博客感兴趣的各位老铁可查看一下!!

那么,我们本文主要是代理搭建来实现一个Spring Boot统一功能处理模块了,当然,这个也是Spring AOP的实战环节,因此,不知道Spring AOP是啥的铁汁,请先看一下前篇博客Spring AOP-CSDN博客做一个简单了解在来研究本文内容!!

本文要实现的目标大概有3个:

  • 统一用户登录权限验证
  • 统一数据格式返回
  • 统一异常处理

用户登录权限校验:

用户登录权限的发展从之前每个方法中自己验证用户登录权限,到现在统一的用户登录验证处理,它是一个逐渐完善和逐渐优化的过程。

最初的用户登录验证:


@RestController
@RequestMapping("/user")
public class User1Controller {

    /**
     * 某方法1
     * @param request
     * @return
     */
    @RequestMapping("/m1")
    public Object method(HttpServletRequest request){
        //有session就获取,没有不会创建
        HttpSession session=request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null){
            //说明已经登录,业务处理
            return true;
        }else {
            //未登录
            return false;
        }
    }

    /**
     * 某方法2
     * @param request
     * @return
     */
    @RequestMapping("/m2")
    public Object method2(HttpServletRequest request){
        //有session就获取,没有不会创建
        HttpSession session=request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null){
            //说明已经登录,业务处理
            return true;
        }else {
            //未登录
            return false;
        }
    }

    /**
     * 其他方法
     */
}

从上述代码可以看出,每个⽅法中都有相同的⽤户登录验证权限,它的缺点是:

  1. 每个⽅法中都要单独写⽤户登录验证的⽅法,即使封装成公共⽅法,也⼀样要传参调⽤和在⽅法中进⾏判断。
  2. 添加控制器越多,调⽤⽤户登录验证的⽅法也越多,这样就增加了后期的修改成本和维护成本。
  3. 这些⽤户登录验证的⽅法和接下来要实现的业务⼏何没有任何关联,但每个⽅法中都要写⼀遍。

所以提供⼀个公共的 AOP ⽅法来进⾏统⼀的⽤户登录权限验证迫在眉睫

Spring AOP用户统一登录验证的问题:

说到统一的用户登录验证,我们想到的第一个方案是Spring AOP前置通知或者环绕通知来实现,具体的实现代码如下:


@Aspect
@Component
public class User1Aspect {

    //定义切点方法controller包下,子孙包下所有类的所有方法
    @Pointcut("execution(* com.example.demo.Controller..*.*(..))")
    public void pointcut(){
        //空方法
    }

    //前置方法
    @Before("pointcut()")
    public void doBefore(){

    }

    //环绕方法
    @Around("pointcut")
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object obj=null;
        System.out.println("Around方法开始执行!");
        try {
            ///执行拦截方法
            obj=joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("Around方法执行结束!");
        return obj;
    }


}

如果要在以上 Spring AOP 的切⾯中实现⽤户登录权限效验的功能,有以下两个问题:

  1. 没办法获取到 HttpSession 对象。
  2. 我们要对⼀部分⽅法进⾏拦截,⽽另⼀部分⽅法不拦截,如注册⽅法和登录⽅法是不拦截的,这样的话排除⽅法的规则很难定义,甚⾄没办法定义。

那么,这个问题该如何解决呢??

Spring拦截器:

对于以上问题Spring中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

  1. 自定义拦截器,实现HandlerInterceptor接口的preHandle(执行具体方法之前的预处理)方法
  2. 将自定义拦截器加入WebMvcConfigurer的addInterceptors方法中

那么有了上述的两个步骤,我们便可以自定义拦截器了!

1.自定义拦截器:

接下来使用代码来实现一个用户登录的权限校验,自定义拦截器是一个普通类,具体的实现代码如下:

@Component //注入Spring框架中
public class loginInterceptor implements HandlerInterceptor {

    //调用目标方法之前执行的方法
    //此方法返回Boolean类型的只,若返回true,表示(拦截器)验证成功,继续走后续流程,执行目标方法
    //                       若返回false,表示(拦截器)执行失败,后续流程和目标方法都不要在执行了

    @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){
            //getAttribute用户的身份信息
            //用户已经登录
            return true;
        }
       // response.sendRedirect("https://www.baidu.com");
        //sendRedirect跳转
        //response.setStatus(401);//401没有权限
        response.setContentType("application/json'charset=utf8");//指定字符集为utf8
       // response.setCharacterEncoding("utf8");
        response.getWriter().println("{\"code\":-1,\"msg\":\"登录失败\",\"data\":\"\"}");
        //getWriter()拿到输出流
        //code状态码
        //msg报错信息
        //另一种方式:使用ObjectMapper给这个对象里面设置属性,然后再将JSON对象转化为字符串就OK🆗了!
        return false;

    }
}

2.将自定义拦截器加入到系统配置:

将上一步中的自定义拦截器加入到系统配置信息中,具体实现代码如下:

@Configuration
public class WebMvcConfigurer implements org.springframework.web.servlet.config.annotation.WebMvcConfigurer {
    @Autowired
    private loginInterceptor loginInterceptor;
    //自定义拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginInterceptor) //将自定义的拦截器添加到系统配置项中
                .addPathPatterns("/**") //拦截所有Url
                .excludePathPatterns("user/login")//排除url/user/login不拦截
                .excludePathPatterns("user/reg")
                .excludePathPatterns("/image/**");//排除image文件夹下所有文件
    }
}

其中:

  • addInterceptor:表示需要拦截的URL,“**”表示拦截任意方法(也就是所有方法)
  • excludePathPatterns:表示需要排除的URL

说明:以上的拦截规则可以拦截此次昂木中的使用的URL,包括静态文件(图片文件,JS和CSS等文件)

练习:

  1. 登录,注册页面不拦截,其他页面都拦截
  2. 当登录成功写入session之后,拦截的页面可正常访问

统一异常处理:

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

  1. 创建一个异常处理类:
    
    @ControllerAdvice //作用:随着Spring的启动而启动,接收Controller中的异常
    public class MyExceptionAdvice {
        
    }
  2. 创建异常检测的类和处理业务方法
    
    @ControllerAdvice //作用:随着Spring的启动而启动,接收Controller中的异常
    @ResponseBody
    public class MyExceptionAdvice {
        //处理空指针异常
        @ExceptionHandler(NullPointerException.class)
        //   @ExceptionHandler异常管理器    NullPointerException.class空指针异常
        public HashMap<String ,Object> doNullPointExcepyion(NullPointerException e){  //企业中不允许这样写
            //e:异常种类
            HashMap<String,Object> result=new HashMap<>();
            result.put("code",-300);//状态码
            result.put("msg","空指针:"+e.getMessage());//状态描述信息
            result.put("data",null);//异常的详细信息
            return result;
        }
        
        //默认的异常处理(当具体的异常匹配不到时,会执行此方法
        @ExceptionHandler(Exception.class)
        //Exception.class所有异常的父类
        public HashMap<String,Object> doException(Exception e){
            HashMap<String,Object> result=new HashMap<>();
            result.put("code",-300);
            result.put("msg","Exception:"+e.getMessage());
            //e.getMessage()异常的详细信息
            result.put("data",null);
            return result;
        }
    }
    
  3. 统一数据返回格式的实现(强制性统一数据返回):在返回数据之前进行数据重写

    统一数据返回格式可以用@ControllerAdvice + ResponseBodyAdvice的方式实现

    
    @ControllerAdvice
    public class ResponseAdvice implements ResponseBodyAdvice {
        @Autowired
        private ObjectMapper objectMapper;
    
        //是否执行beforeBobyWrite方法;true=执行,重写返回结果
        @Override
        public boolean supports(MethodParameter returnType,Class converterType){
            return true;
        }
    
        //返回数据之前进行数据重写
        @Override
        @SneakyThrows
        public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                      MediaType selectedContentType, Class selectedConverterType,
                                      ServerHttpRequest request, ServerHttpResponse response) {
            //规定:标准返回格式
            //HashMap<String,Object>-->code msg  data
            //判断是否为统一的数据格式
            if (body instanceof HashMap) {
                //判断body的类型是否为HashMap
                return body;
            }
            //重写返回结果,让其返回一个统一的数据格式
            HashMap<String,Object> result=new HashMap<>();
            result.put("code",200);
            result.put("data",body);
            result.put("msg"," ");//状态码的描述
    
            if (body instanceof String){
                //返回一个将对象转化为JSON String字符串
                return objectMapper.writeValueAsString(result);
                //一样的写法
                //return "{\"code\":200,\"msg\":\",\"data\":\""+body+"\"}";
                //在统一数据重写时,单独处理String类型,让其返回一个Spring字符串,而非HashMap
            }
    
            return result;
        }
    }
    

综合练习:用户登录+拦截器

  1. 实现注册和登录功能
  2. 添加统一的错误处理(@ControllerAdvice)
  3. 添加登录拦截器( WebMvcConfigurer + HandlerInterceptor)
  4. 用户登录之后能到欢迎页面,未登录直接跳到登录页面
  5. 添加统一的返回格式(包含:status,data,msg等字段)

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL Server 2016(为数据表Porducts添加数据)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;并已经创建了数据库PM。 2、需求描述。 在数据库PM中创建表products&#xff0c;"编号"列的值自动增长并为主键。然后使用T-SQL语句为表格插入如下数据。 3、实验步骤。 1、使用SSMS管理工…

第九节HarmonyOS 常用基础组件1-Text

一、组件介绍 组件&#xff08;Component&#xff09;是界面搭建与显示的最小单位&#xff0c;HarmonyOS ArkUI声名式为开发者提供了丰富多样的UI组件&#xff0c;我们可以使用这些组件轻松的编写出更加丰富、漂亮的界面。 组件根据功能可以分为以下五大类&#xff1a;基础组件…

如何优雅的进行业务分层

1.什么是应用分层 说起应用分层&#xff0c;大部分人都会认为这个不是很简单嘛 就controller&#xff0c;service, mapper三层。 看起来简单&#xff0c;很多人其实并没有把他们职责划分开&#xff0c;在很多代码中&#xff0c;controller做的逻辑比service还多,service往往当…

C/C++,图算法——求强联通的Tarjan算法之源程序

1 文本格式 #include <bits/stdc.h> using namespace std; const int maxn 1e4 5; const int maxk 5005; int n, k; int id[maxn][5]; char s[maxn][5][5], ans[maxk]; bool vis[maxn]; struct Edge { int v, nxt; } e[maxn * 100]; int head[maxn], tot 1; vo…

BUUCTF-MISC-第二题

下载并打开题目附件 图片是GIF格式动态图片 动态过程会时不时弹出flag 但是速度很快 我们需要想办法去拦截 使用Stegsolve工具进一步分析Analyse->Frame Browser 对图片进行锁帧操作 21帧51帧79帧得到flag&#xff1a;flag{he11ohongke} 本题意义&#xff1a; 对MISC图片隐写…

CSS 绝对定位问题和粘性定位介绍

目录 1&#xff0c;绝对定位问题1&#xff0c;绝对定位元素的特性2&#xff0c;初始包含块问题 2&#xff0c;粘性定位注意点&#xff1a; 1&#xff0c;绝对定位问题 1&#xff0c;绝对定位元素的特性 display 默认为 block。所以行内元素设置绝对定位后可直接设置宽高。脱离…

JS 实现一键复制文本内容

1、演示&#xff1a; 2、代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>一键复制</title&g…

树莓派搭建开发环境

背景 自从上次心血来潮给树莓派装完系统&#xff0c;一直没想好怎么具体使用它的场景&#xff0c;它就这样默默地躺在抽屉吃灰了一年 再次想起它&#xff0c;是一个周日的下午&#xff1a;收到之前在腾讯云买的云服务器快过期的提醒&#xff0c;一个4核8G内存的ubuntu&#x…

windows下如何搭建属于自己的git服务器?

windows下如何搭建属于自己的git服务器&#xff1f; 工具准备&#xff08;此章节为网上摘要&#xff0c;忘记出自哪里了&#xff0c;大家自行参考&#xff09;实操步骤 工具准备&#xff08;此章节为网上摘要&#xff0c;忘记出自哪里了&#xff0c;大家自行参考&#xff09; …

华为云之快速部署FTP站点

华为云之快速部署FTP站点 一、本次实践介绍1.1 实践环境简介1.2 本次实践目的 二、vsftpd介绍2.1 vsftpd简介2.2 vsftpd特点 三、环境准备工作3.1 预置实验环境3.2 查看预置环境信息3.3 登录华为云3.4 查看弹性云服务器状态3.5 查看弹性公网IP地址3.6 ssh登录弹性云服务器3.6 查…

A++ 敏捷开发-1 如何改善

1 如何改善 敏捷开发过程改进案例 5月 A公司一直专门为某电信公司提供针对客服、线上播放等服务服务。 张工是公司的中层管理者&#xff0c;管理好几个开发团队&#xff0c;有5位项目经理向他汇报。 他听说老同学的团队都开始用敏捷开发&#xff0c;很感兴趣&#xff0c;便参…

【linux】日志有哪些

Linux系统日志主要有以下几种类型&#xff1a; 内核及系统日志&#xff1a;这种日志数据由系统服务rsyslog统一管理&#xff0c;根据其主配置文件/etc/rsyslog.conf中设置决定内核消息及各种系统程序消息记录到什么位置。/var/log/message&#xff1a;该日志文件存放了内核消息…

VScode异常处理 (因为在此系统上禁止运行脚本)

在使用 VScode 自带程序终端的时候会报出"系统禁止脚本运行的错误" 这是由于 Windows PowerShell执行策略导致的 解决办法 管理员身份运行 Windows PowerShell执行&#xff1a;get-ExecutionPolicy1&#xff0c;显示Restricted2执行&#xff1a;Set-ExecutionPoli…

Zabbix 6.0部署+自定义监控项+自动发现与自动注册+部署zabbix代理服务器

Zabbix 6.0 Zabbix 6.0一、关于zabbix1、什么是zabbix2、zabbix工作原理3、zabbix 6.0 特性4、zabbix 6.0 功能组件 二、Zabbix 6.0 部署1、 部署 zabbix 服务端(1) 部署 Nginx PHP 环境并测试(2) 部署数据库(3) 编译安装 zabbix server 服务端(4) 部署 Web 前端&#xff0c;进…

Vue3 的 inject 和 provide (附源码)

一&#xff1a;前言 在前端项目中牵扯的最多的莫过于组件之间的传值了&#xff0c;除了最最常用的 props 和 emit&#xff0c;其实在 Vue 中还额外提供了另外几种方法。今天分享一种组件之间通信的方法&#xff1a;provide 和 inject。 二&#xff1a;使用 1、目录结构 以下是…

C# WPF上位机开发(乘法计算小软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 上面一篇文章&#xff0c;我们简单了解了怎么用xaml来设计界面。和传统的c# form不一样&#xff0c;它除了可以通过拖拽的方法来实现界面的编写之外…

【JavaSE学习专栏】第04篇 Java面向对象

文章目录 1 面向过程&面向对象2 类和对象2.1 对象的特征2.2 java类及类的成员2.3 类的语法格式 3 创建与初始化对象3.1 类的成员之一&#xff1a;属性3.2 类的成员之二&#xff1a;方法3.3 类的成员之三&#xff1a;构造器&#xff08;构造方法&#xff09;3.3.1 无参构造方…

【算法刷题】Day10

文章目录 15. 三数之和题干&#xff1a;算法原理&#xff1a;1、排序 暴力枚举 利用set 去重2、排序 双指针 代码&#xff1a; 18. 18. 四数之和题干&#xff1a;算法原理&#xff1a;1、排序 暴力枚举 利用set 去重2、排序 双指针 代码&#xff1a; 15. 三数之和 原题链…

CentOS 部署 WBO 在线协作白板

1&#xff09;WBO 白板工具介绍 1.1&#xff09;WBO 白板简介 WBO 是一个自由和开源的在线协作白板。它允许多个用户同时在一个虚拟的大型白板上画图。该白板对所有线上用户实时更新&#xff0c;并且状态始终保持。它可以用于许多不同的目的&#xff0c;包括艺术、娱乐、设计和…

生物教师个人简历(精选21篇)

以下21篇简历内容以生物教师招聘需求为背景制作&#xff0c;大家可以灵活借鉴&#xff0c;希望能帮助大家在众多候选人中脱颖而出。 生物教师个人简历下载&#xff08;在线制作&#xff09;&#xff1a;百度幻主简历或huanzhuv.com 生物老师简历1&#xff1a; 求职意向 求职…