【Spring AOP】如何统一“拦截器校验、数据格式返回、异常返回”处理?

news2025/1/22 12:52:07

目录

一、Spring  拦截器

1.1、背景

1.2、实现步骤

1.3、拦截原理

二、 统一url前缀路径

2.1、方法一:在系统的配置文件中设置

2.2、方法二:在 application.properies 中配置

三、统一异常处理

四、统一返回数据返回格式处理

4.1、背景

4.2、具体实现


一、Spring  拦截器


1.1、背景

在原生的 Spring AOP 中实现统一的拦截的难点在于:1.定义拦截规则(表达式)很难,2.在切面类中拿到 HttpSession 比较难;如何解决这两个难点呢?使用拦截器!

1.2、实现步骤

实现一个普通的拦截器关键在于以下两步:

  1. 实现 HandlerInterceptor 接口,重写 preHeadler 方法,在方法中编写自己的业务代码。
  2. 将拦截器添加到配置文件中,设置拦截规则。

具体的,首先步骤一,例如要实现一个用户登录判断,就需要创建一个类,这里起名叫LoginInterceptor 类,实现 HandlerInterceptor 接口,重写 preHeadler 方法(此方法返回的是以个 boolean 类型,如果为 true 表示验证成功,可以继续执行后面的流程,若是 false 表示验证失败,后面的流程就不执行了),通过是否可以获取到 Session 信息判断用户是否已经登陆,来返回 true 或 false。

a)实现 HandlerInterceptor 接口,重写 preHeadler 方法,如下代码:

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//登录拦截器
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 此方法返回一个 boolean,若为 true 表示验证成功,否则验证失败,后面的流程不能执行了
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户登录业务判断
        HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute("userinfo") != null) {
            //说明用户已经登陆
            return true;
        }
        //可以调整登录页面,或者 返回一个 401/403 没有权限
        response.sendRedirect("/login.html");
        return false;
    }
}

b)将拦截器添加到配置文件中,设置拦截规则

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") //连接所有请求,值得注意的是,这里不能只写一个 *,一个 * 表示一级路径
                .excludePathPatterns("/user/login") //不拦截的 url
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/**/*.html"); //不拦截所有的页面
    }
}

注意:

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

说明:以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件、JS 和 CSS 等⽂件)

1.3、拦截原理

二、 统一url前缀路径


2.1、方法一:在系统的配置文件中设置

具体的,重写 WebMvcConfigurer 接口下的 configurePathMatch 方法,例如修改所有请求url添加前缀 /zhangsan ,如下代码:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("/zhangsan", c -> true);
    }

}

2.2、方法二:在 application.properies 中配置

例如修改所有请求url添加前缀 /zhangsan,如下代码:

server.servlet.context-path=/zhangsan

三、统一异常处理


统一异常处理是通过如下两个注解结合实现的:

  • @ControllerAdvice:表示控制器通知类。
  • @ExceptionHandler:表示异常处理器。

两个结合表示出现异常的时候执行某个通知方法,具体的步骤如下:

  1. 创建一个类,标识上 @ControllerAdvice;
  2. 在方法上添加 @ExceptionHandler;

如下代码:

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;

@ControllerAdvice
@ResponseBody
public class MyExHandler {

    /**
     * 拦截所有的空指针异常,进行统一的数据返回
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class) //这里也可以根据实际情况,填写不同的异常
    public HashMap<String, Object> nullException(NullPointerException e) {
        HashMap<String, Object> reslut = new HashMap<>();
        reslut.put("code", "-1");
        reslut.put("msg", "空指针异常" + e.getMessage());
        reslut.put("data", null);
        //这里返回 HashMap ,就相当于项前端返回了一个 JSON 格式的数据
        return reslut;
    }

}

四、统一返回数据返回格式处理


4.1、背景

为什么要统一数据返回格式处理?例如以下几个原因:

  • 方便前端程序员更好的接收和解析后端返回的数据;
  • 降低约定前后端交互接口的成本,按照某种格式实现即可,因为所有的接口都是这样返回的。
  • 有利于项目的统一数据的维护和修改。

4.2、具体实现

统一数据格式返回的实现需要以下两个步骤:

  1. 创建一个类,并添加 @ControllerAdvice。
  2. 实现 ResponseBodyAdvice 接口,重写 supports 和  beforeBodyWrite。

Ps:

1、 supports 方法不用编写业务逻辑,而是像一个控制器一样,返回 true 则执行 beforeBodyWrite 方法,反之则不执行。

2、beforeBodyWrite 方法就是用来实现统一对象的。

具体的如下:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    //将 java 对象转化成 JSON 格式
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 此方法返回 true 则执行下面的 beforeBodyWrite 方法,反之则不执行
     * @param returnType
     * @param converterType
     * @return
     */
    @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) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("msg", "");
        result.put("data",body);
        // 这里需要进行特殊处理,因为 String 在转换的时候报错
        if(body instanceof String) {
            try {
                return objectMapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

代码中为什么要进行特殊处理?(最易出错!)

在 java 程序中, String 是一个最特殊的类型(既不是基础类型也不是对象),并且在重写方法时也很特殊,除了 String 其他的都是用一个格式化工具,而 String 用的是自己的一套格式化工具,因此在转换成 HashMap 的时候还没有被加载好,而其他的转换器已经加载好了,最后就会引发如下异常:

因此就要判断 body 是否为 String ,若为 String 类型,就要进行特殊处理,使用 JSON 的writeValueAsString 方法将 Java 对象转换成 JSON 格式再返回。

 

 

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

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

相关文章

PTA:L1-025 正整数A+B、L1-026 I Love GPLT、L1-027 出租(C++)

目录 L1-025 正整数AB 问题描述&#xff1a; 实现代码&#xff1a; L1-026 I Love GPLT 问题描述&#xff1a; 实现代码&#xff1a; L1-027 出租 问题描述&#xff1a; 实现代码&#xff1a; 原理思路&#xff1a; 出租那道题有点意思哈 L1-025 正整数AB 问题描述…

【Java学习笔记】13.Java StringBuffer 和 StringBuilder 类

Java StringBuffer 和 StringBuilder 类 当对字符串进行修改的时候&#xff0c;需要使用 StringBuffer 和 StringBuilder 类。 和 String 类不同的是&#xff0c;StringBuffer 和 StringBuilder 类的对象能够被多次的修改&#xff0c;并且不产生新的未使用对象。 在使用 St…

Tomcat8安装

1、前置环境 Tomcat 8 对应jdk 1.8 版本&#xff1b;如果你的jdk版本是8以上&#xff0c;则安装对应的tomcat版本。 jdk8 官方下载安装时&#xff0c;先安装jdk版本&#xff0c;最后单独安装jre。所以电脑会有两套jre&#xff0c;一套是jdk中的jre&#xff0c;位于 \jre 目录下…

客户案例|三强联手,深度集成,实现四方共赢

关键发现&#xff1a; 用户痛点&#xff1a;以现有ERP系统台账表单模式管理设备&#xff0c;已经不能满足伯恩业务增长所需的设备管理优化与革新的要求。 解决方案&#xff1a;利用西门子Mendix低代码平台与SAP PM模块进行集成开发的联合解决方案&#xff0c;为实现客户设备资…

3.8 并查集

并查集 题目链接 用途 维护集合 将两个集合合并询问两个元素是否在一个集合当中 实现思路 用树的形式维护集合每个集合用一棵树表示&#xff0c;树根的编号就是整个集合的编号&#xff0c;每个节点存储他的父节点&#xff0c;p[x]表示节点x的父节点判断树根的方法:p[x]x求…

运维视角:rabbitmq教程(三)镜像集群

上期回顾 RabbitMQ集群中节点包括内存节点、磁盘节点。内存节点就是将所有数据放在内存&#xff0c;磁盘节点将数据放在磁盘上。如果在投递消息时&#xff0c;打开了消息的持久化&#xff0c;那么即使是内存节点&#xff0c;数据还是安全的放在磁盘。那么内存节点的性能只能体现…

如何编写BI项目之ETL文档

XXXXBI项目之ETL文档 xxx项目组 ------------------------------------------------1---------------------------------------------------------------------- 目录 一 、ETL之概述 1、ETL是数据仓库建构/应用中的核心…

Linux基础命令-kill向进程发送信号

Linux基础命令-setfacl设置文件ACL策略规则 Kill 一.命令介绍 先使用帮助文档查看命令的信息 NAME kill - terminate a process kill命令的主要功能就是向进程发送信号&#xff0c;这里我们主要用来终止结束进程的&#xff0c;与它的英文单词含义相同&#xff0c;在Linux系统…

matlab在管理学中的应用简matlab基础【三】

规划论及MATLAB计算 1、线性规划 问题的提出 例1. 某工厂在计划期内要安排甲、乙两种产品的生产&#xff0c;已知生产单位产品所需的资源A、B、C的消耗以及资源的计划期供给量&#xff0c;如下表&#xff1a; 问题&#xff1a;工厂应分别生产多少单位甲、乙产品才能使工厂获…

相亲交友直播APP源码

一、什么是亲交友直播APP源码&#xff1f; 亲交友直播APP源码是一款婚恋交友类型的APP&#xff0c;可以帮助单身男女在网络平台就可以进行相亲交友。APP源码分两端&#xff0c;一端是用户端&#xff0c;另外一端是后台端。采用的技术&#xff0c;前端是安卓IOS&#xff0c;后端…

SCI期刊写作必备(二):代码|手把手绘制目标检测领域YOLO论文常见的性能对比折线图,一键生成YOLOv7等主流论文同款图表,包含多种不同功能风格对比图表

绘制一个原创属于自己的YOLO模型性能对比图表 具体绘制操作参考:(附Python代码,直接一键生成,精度对比图表代码 ) 只需要改动为自己的mAP、Params、FPS、GFlops等数值即可,一键生成 多种图表风格📈,可以按需挑选 文章目录 绘制一个原创属于自己的YOLO模型性能对比图…

二、HTTP协议02

文章目录一、HTTP状态管理Cookie和Session二、HTTP协议之身份认证三、HTTP长连接与短连接四、HTTP中介之代理五、HTTP中介之网关六、HTTP之内容协商七、断点续传和多线程下载一、HTTP状态管理Cookie和Session HTTP的缺陷无状态。Cookie和Session就用来弥补这个缺陷的。 Cooki…

Kafka 位移主题

Kafka 位移主题位移格式创建位移提交位移删除位移Kafka 的内部主题 (Internal Topic) : __consumer_offsets (位移主题&#xff0c;Offsets Topic) 老 Consumer 会将位移消息提交到 ZK 中保存 当 Consumer 重启后&#xff0c;能自动从 ZK 中读取位移数据&#xff0c;继续消费…

Kafka和RabbitMQ有哪些区别,各自适合什么场景?

目录标题1. 消息的顺序2. 消息的匹配3. 消息的超时4. 消息的保持5. 消息的错误处理6. 消息的吞吐量总结1. 消息的顺序 有这样一个需求&#xff1a;当订单状态变化的时候&#xff0c;把订单状态变化的消息发送给所有关心订单变化的系统。 订单会有创建成功、待付款、已支付、已…

C++面向对象编程之三:初始化列表、类对象作为类成员、静态成员

初始化列表C提供了初始化列表语法&#xff0c;可以用于成员属性初始化。语法规则&#xff1a;无参构造函数():属性1(值1), 属性2(值2), ... { }有参构造函数(形参1, 形参2, ...):属性1(形参1), 属性2(形参2), ... { }example&#xff1a;写一个怪物类&#xff0c;有怪物id和血量…

【产品设计】ToB 增删改查显算传

入职培训时技术leader说&#xff1a;“我不需要你们太聪明&#xff0c;做好基础的增删改查就可以了。”看似很简单的活&#xff0c;要做好并不容易。基础的坑在哪里呢&#xff1f; 一、 增&#xff08;新增、创建、导入&#xff09; 1. 明确表字段类型 新增的业务是由不同类型…

Android Studio开发APP

1.下载Android Studio 官网下载:Android Studio for Window ... 百度云下载:android-studio-bundle-141.1903250-windows.exe Android Studio 是谷歌推出的一个Android集成开发工具,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用…

js 时间戳转换,全网最牛业务逻辑解析,逐行解析代码,千万不要错过,里面有些错误场景你可能还不知道

一、注意事项 1、js只识别13位的时间戳 2、10位的是unix时间戳 二、小试牛刀--日期类型转换成时间戳 可先将js字符串转换为date类型&#xff0c;再转换为时间戳类型。 字符串格式为 yyyy-MM-dd HH:mm:ss或者yyyy-MM-dd //把字符串转换成时间格式 let date new Date(2023-11-12…

(二十四)操作系统-吸烟者问题

文章目录一、问题描述二、问题分析1&#xff0e;关系分析2&#xff0e;整理思路3&#xff0e;设置信号量三、实现四、总结一、问题描述 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要…

String类的底层原理和版本演变

1 String类的底层演变&#xff08;1&#xff09; JDK8以及之前版本 &#xff08;2&#xff09;JDK9以及之后版本 javaJDK8的字符串存储在char类型的数组里面&#xff0c;在java中&#xff0c;一个char类型占两个字节。但是很多时候&#xff0c;一个字符只需要一个字节就可存储&…