十、SpringBoot 统⼀功能处理【拦截器、统一数据返回格式、统一异常处理】

news2024/11/15 12:42:20

十、SpringBoot 统⼀功能处理

    • 1. 拦截器【HandlerInterceptor、WebMvcConfig】
      • 1.1 拦截器快速⼊⻔
        • ⾃定义拦截器:实现HandlerInterceptor接⼝,并重写其所有⽅法
        • 注册配置拦截器:实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法
      • 1.2 拦截器详解
        • 1.2.1 拦截路径
        • 1.2.2 拦截器执⾏流程
      • 1.3 登录校验
        • 1.3.1 定义拦截器
        • 1.3.2 注册配置拦截器
    • 2. 统⼀数据返回格式【ControllerAdvicd、ResponseAdviced】
      • 2.1 快速⼊⻔
    • 3. 统⼀异常处理【@ControllerAdvice + @ExceptionHandler】

本节⽬标

  1. 掌握拦截器的使⽤, 及其原理
  2. 学习统⼀数据返回格式和统⼀异常处理的操作
  3. 了解⼀些Spring的源码

1. 拦截器【HandlerInterceptor、WebMvcConfig】

上个章节我们完成了强制登录的功能, 后端程序根据Session来判断⽤⼾是否登录, 但是实现⽅法是⽐较⿇烦的
• 需要修改每个接⼝的处理逻辑
• 需要修改每个接⼝的返回结果
• 接⼝定义修改, 前端代码也需要跟着修改
有没有更简单的办法, 统⼀拦截所有的请求, 并进⾏Session校验呢, 这⾥我们学习⼀种新的解决办法: 拦截器

1.1 拦截器快速⼊⻔

  • 什么是拦截器?
    拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码.
    也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也可以在⽤⼾请求前阻⽌其执⾏.
    在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作, ⽐如通过拦截器来拦截前端发来的请求, 判断Session中是否有登录⽤⼾的信息. 如果有就可以放⾏, 如果没有就进⾏拦截.

在这里插入图片描述
下⾯我们先来学习下拦截器的基本使⽤.
拦截器的使⽤步骤分为两步:

  1. 定义拦截器
  2. 注册配置拦截器
⾃定义拦截器:实现HandlerInterceptor接⼝,并重写其所有⽅法
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
 
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		log.info("LoginInterceptor ⽬标⽅法执⾏前执⾏..");
		return true;
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		log.info("LoginInterceptor ⽬标⽅法执⾏后执⾏");
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		log.info("LoginInterceptor 视图渲染完毕后执⾏,最后执⾏");
	}
}

• preHandle()⽅法:⽬标⽅法执⾏前执⾏. 返回true: 继续执⾏后续操作; 返回false: 中断后续操作.
• postHandle()⽅法:⽬标⽅法执⾏后执⾏
• afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图, 暂不了解)

注册配置拦截器:实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法
@Configuration
public class WebConfig implements WebMvcConfigurer {
	//⾃定义的拦截器对象
	@Autowired
	private LoginInterceptor loginInterceptor;
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册⾃定义拦截器对象
		registry.addInterceptor(loginInterceptor)
			.addPathPatterns("/**");
	//设置拦截器拦截的请求路径( /** 表⽰拦截所有请求)
	}
}

启动服务, 试试访问任意请求, 观察后端⽇志
在这里插入图片描述
可以看到preHandle ⽅法执⾏之后就放⾏了, 开始执⾏⽬标⽅法, ⽬标⽅法执⾏完成之后执⾏postHandle和afterCompletion⽅法.

我们把拦截器中preHandle⽅法的返回值改为false, 再观察运⾏结果在这里插入图片描述
可以看到, 拦截器拦截了请求, 没有进⾏响应.

1.2 拦截器详解

拦截器的⼊⻔程序完成之后,接下来我们来介绍拦截器的使⽤细节。拦截器的使⽤细节我们主要介绍
两个部分:

  1. 拦截器的拦截路径配置
  2. 拦截器实现原理
1.2.1 拦截路径

拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.
我们在注册配置拦截器的时候, 通过 addPathPatterns() ⽅法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求.
上述代码中, 我们配置的是 /** , 表⽰拦截所有的请求.
⽐如⽤⼾登录校验, 我们希望可以对除了登录之外所有的路径⽣效.

import org.springframework.beans.factory.annotation.Autowired;
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 WebConfig implements WebMvcConfigurer {
	//⾃定义的拦截器对象
	@Autowired
	private LoginInterceptor loginInterceptor;
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册⾃定义拦截器对象
		registry.addInterceptor(loginInterceptor)
			.addPathPatterns("/**")
			.excludePathPatterns("/user/login");
			//设置拦截器拦截的请求路径
		(/** 表⽰拦截所有请求)
	}
1.2.2 拦截器执⾏流程

在这里插入图片描述
在这里插入图片描述

  1. 添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法,这个⽅法需要返回⼀个布尔类型的值. 如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的⽅法. 如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).
  2. controller当中的⽅法执⾏完毕后,再回过来执⾏postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据.

1.3 登录校验

学习拦截器的基本操作之后,接下来我们需要完成最后⼀步操作:通过拦截器来完成图书管理系统中的登录校验功能

1.3.1 定义拦截器

从session中获取⽤⼾信息, 如果session中不存在, 则返回false,并设置http状态码为401, 否则返回true.

@Slf4j
@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(Constants.SESSION_USER_KEY) != null) {
			return true;
		}
		response.setStatus(401);
		return false;
	}
}

http状态码401: Unauthorized
Indicates that authentication is required and was either not provided or has failed. If the
request already included authorization credentials, then the 401 status code indicates that
those credentials were not accepted.
中⽂解释: 未经过认证. 指⽰⾝份验证是必需的, 没有提供⾝份验证或⾝份验证失败. 如果请求已经包
含授权凭据,那么401状态码表⽰不接受这些凭据

1.3.2 注册配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
	//⾃定义的拦截器对象
	@Autowired
	private LoginInterceptor loginInterceptor;
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册⾃定义拦截器对象
		registry.addInterceptor(loginInterceptor)
		.addPathPatterns("/**")//设置拦截器拦截的请求路径(/**表⽰拦截所有请求)
		.excludePathPatterns("/user/login")//设置拦截器排除拦截的路径
		.excludePathPatterns("/**/*.js") //排除前端静态资源
		.excludePathPatterns("/**/*.css")
		.excludePathPatterns("/**/*.png")
		.excludePathPatterns("/**/*.html");
 }
}

2. 统⼀数据返回格式【ControllerAdvicd、ResponseAdviced】

强制登录案例中, 我们共做了两部分⼯作

  1. 通过Session来判断⽤⼾是否登录
  2. 对后端返回数据进⾏封装, 告知前端处理的结果

回顾
后端统⼀返回结果

@Data
public class Result<T> {
 private int status;
 private String errorMessage;
 private T data;
}

后端逻辑处理

@RequestMapping("/getListByPage")
public Result getListByPage(PageRequest pageRequest) {
	log.info("获取图书列表, pageRequest:{}", pageRequest);
	//⽤⼾登录, 返回图书列表
	PageResult<BookInfo> pageResult = bookService.getBookListByPage(pageRequest);
	log.info("获取图书列表222, pageRequest:{}", pageResult);
	return Result.success(pageResult);
}

Result.success(pageResult) 就是对返回数据进⾏了封装
拦截器帮我们实现了第⼀个功能, 接下来看SpringBoot对第⼆个功能如何⽀持

2.1 快速⼊⻔

统⼀的数据返回格式使⽤ @ControllerAdvice 和ResponseBodyAdvice 的⽅式实现@ControllerAdvice 表⽰控制器通知类
添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加@ControllerAdvice 注解

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
	@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) {
		return Result.success(body);
	}
}

supports⽅法: 判断是否要执⾏beforeBodyWrite⽅法. true为执⾏, false不执⾏. 通过该⽅法可以选择哪些类或哪些⽅法的response要进⾏处理, 其他的不进⾏处理.

从returnType获取类名和⽅法名

//获取执⾏的类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
//获取执⾏的⽅法
Method method = returnType.getMethod();

beforeBodyWrite⽅法: 对response⽅法进⾏具体操作处理

@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
	//返回结果更加灵活
	if (body instanceof Result){
		return body;
	}
	//如果返回结果为String类型, 使⽤SpringBoot内置提供的Jackson来实现信息的序列化
	if (body instanceof String){
		return mapper.writeValueAsString(Result.success(body));
	}
	return Result.success(body);
}

3. 统⼀异常处理【@ControllerAdvice + @ExceptionHandler】

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件
具体代码如下

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
 @ExceptionHandler
 public Object handler(Exception e) {
	 return Result.fail(e.getMessage());
 }
}

类名, ⽅法名和返回值可以⾃定义, 重要的是注解
接⼝返回为数据时, 需要加 @ResponseBody 注解

以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类), 就返回⼀个 Result的对象, Result对象的设置参考 Result.fail(e.getMessage())


public static Result fail(String msg) {
 Result result = new Result();
 result.setStatus(ResultStatus.FAIL);
 result.setErrorMessage(msg);
 result.setData("");
 return result;
}

我们可以针对不同的异常, 返回不同的结果


@ResponseBody
@ControllerAdvice
public class ErrorAdvice {
 @ExceptionHandler
 public Object handler(Exception e) {
 return Result.fail(e.getMessage());
 }
 @ExceptionHandler
 public Object handler(NullPointerException e) {
 return Result.fail("发⽣NullPointerException:"+e.getMessage());
 }
 @ExceptionHandler
 public Object handler(ArithmeticException e) {
 return Result.fail("发⽣ArithmeticException:"+e.getMessage());
 }
}

模拟制造异常:

@RequestMapping("/test")
@RestController
public class TestController {
 @RequestMapping("/t1")
 public String t1(){
 return "t1";
 }
 @RequestMapping("/t2")
 public boolean t2(){
 int a = 10/0; //抛出ArithmeticException
 return true;
 }
 @RequestMapping("/t3")
 public Integer t3(){
 String a =null;
 System.out.println(a.length()); //抛出NullPointerException
 return 200;
 }
}

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

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

相关文章

从C到C++入门篇(三)引用;引用的本质

Reference & 引用 变量名&#xff0c;本身是一段内存的引用&#xff0c;即别名(alias)。此处引入的引用&#xff0c;是为己有变 量起一个别名。 int a 500; //变量名.实质是一段内存空间的别名 (int)0x0002345500; 引用的规则 引用&#xff0c;是一种关系型声明&#xff0…

【电路笔记】-D类放大器

D类放大器 文章目录 D类放大器1、概述2、D类放大器介绍3、调制4、放大5、滤波6、效率7、总结1、概述 在之前的文章中,放大器的导通角与其效率之间建立了重要的联系。 事实上,基于高导通角的放大器提供非常好的线性度,例如 A 类放大器,但效率非常有限,通常约为 20% 至 30%…

MATLAB基础:数据和变量

今天我们开始学习MATLAB基础知识 1、常用非运算符及其作用 1、“,” 作为程序运行的分隔符&#xff0c;起到分隔语句的作用 2、“;” 同样作为分隔符&#xff0c;与“,”不同的是“;”会在程序运行时隐藏该行语句 如下图&#xff1a; 3、“...” 三个英文句点表示续行符…

深入解析食堂采购系统源码开发:从零开始构建供应链采购管理APP

今天&#xff0c;笔者将与大家共同探讨食堂采购系统源码开发&#xff0c;从零开始构建一个高效的供应链采购管理APP。 一、需求分析 在开始开发之前&#xff0c;首先需要进行详细的需求分析。这一步至关重要&#xff0c;因为它直接影响到系统的功能和架构设计。对于食堂采购系…

达梦数据库系列—30. DTS迁移Mysql到DM

目录 1.MySQL 源端信息 2.DM 目的端信息 3.迁移评估 4.数据库迁移 4.1源端 MySQL 准备 4.2目的端达梦准备 初始化参数设置 兼容性参数设置 创建迁移用户和表空间 4.3迁移步骤 创建迁移 配置迁移对象及策略 开始迁移 对象补迁 5.数据校验 统计 MySQL 端对象及数…

BUG与测试用例设计

一.软件测试的生命周期 需求分析→测试计划→测试设计,测试开发→测试执行→测试评估→上线→运行维护 二.BUG 1.bug的概念 (1)当且仅当规格说明(需求文档)是存在的并且正确,程序与规格说明之间的不匹配才是错误. (2)当需求规格说明书没有提到的功能,判断标准以最终用户为准…

vscode调试nextjs前端后端程序、nextjs api接口

最近有一个项目使用了nextjs框架&#xff0c;并且使用nextjs同时实现了前后端&#xff0c;由于之前前后端都是分离的&#xff0c;前端的调试可以通过在代码种添加debugger或者直接在浏览器中打断点实现&#xff0c;现在想调试后端接口&#xff0c;前面的方式就不适用了。故研究…

【RaspberryPi】树莓派Matlab/Simulink支持包安装与使用

官网支持与兼容性 Raspberry Pi Support from MATLAB - Hardware Support - MATLAB & Simulink Raspberry Pi Support from Simulink - Hardware Support - MATLAB & Simulink Matlab与树莓派兼容性 Simulink与树莓派兼容性 树莓派Matlab&Simulink RaspberryPi支…

项目实战1(30小时精通C++和外挂实战)

项目实战1&#xff08;30小时精通C和外挂实战&#xff09; 01-MFC1-图标02-MFC2-按钮、调试、打开网页05-MFC5-checkbox及按钮绑定对象06--文件格式、OD序列号08-暴力破解09-CE10-秒杀僵尸 01-MFC1-图标 这个外挂只针对植物大战僵尸游戏 开发这个外挂&#xff0c;首先要将界面…

RK3399 Linux 系统,接i2c外设,时好时坏(三)其中一个解决问题方法

在 RK3399 平台上,连接 I2C 设备时,有时可能会遇到时好时坏的问题。这种情况往往与引脚的配置有关。在本文中,我们将讨论如何通过调整引脚的上下拉配置来解决这个问题。 目前瑞芯微芯片,需要调节i2c驱动电流能力的,有以下芯片: 具体来说,我们将把 I2C1 的引脚配置中的…

SpringSecurity专题

目录 一&#xff1a;认证授权 什么是认证授权&#xff1a; 二&#xff1a;权限数据模型 RBAC权限数据模型 2.1基于角色访问权限控制 2.2基于资源访问权限控制 常见的认证方式 1.Cookie-Session 2.jwt令牌无状态认证 三&#xff1a;JWT 1.JWT的组成 2.JWT的使用 四&…

【STC32G12K128开发板】第3-7讲:声音探测传感器

第3-7讲&#xff1a;声音探测传感器 学习目的了解声音探测传感器模块的作用。掌握单片机编程读取声音探测传感器模块引脚输出状态&#xff0c;从而判断周围环境声音强度有没有达到设置的阈值。 声音探测传感器简介 声音探测传感器模块对环境声音强度敏感&#xff0c;常用来检测…

解决Linux桌面初始化问题

问题 启动vnc桌面&#xff0c;提示问题 定位 从[t]csh手册 可以看到&#xff0c;其初始化流程 经定位&#xff0c;是.cshrc的这段代码存在&#xff0c;导致桌面初始化异常。 [wanlin.wangicinfra-cn-172-16-0-115 ~]$ cat .cshrc ...部分省略... # Environment for anac…

模拟电子技术-实验五 单管放大电路仿真实验

实验五 单管放大电路仿真实验 一&#xff0e;实验类型 二&#xff0e;实验目的 1、熟悉multisim的仿真实验法&#xff0c;熟悉multisim中双踪示波器和信号发生器的设置和使用方法。学习电压表的使用方法。 2、熟悉放大电路的基本测量方法&#xff0c;了解使放大电路不失真地…

Spring中@PostConstruct注解的使用

1.描述 1.1 背景 最近在做一个系统交互日志模块&#xff0c;要监控一个http请求&#xff0c;并记录请求与响应日志。项目中使用RestTemplate来发送http请求&#xff0c;所以打算给RestTemplate设置拦截器&#xff0c;来进行自定义操作。但是&#xff0c;只对当前类生效&#x…

《昇思25天学习打卡营第23天|RNN实现情感分类》

使用RNN进行情感分类&#xff1a;基于IMDB数据集的LSTM应用 引言 情感分析是自然语言处理&#xff08;NLP&#xff09;中的一个重要应用&#xff0c;广泛用于电影评论、社交媒体等文本数据的情感分类任务。本文将介绍如何使用递归神经网络&#xff08;RNN&#xff09;实现情感…

InternLM学习笔记

入门岛 1. Linux基础知识 2. Python 基础知识 from collections import Countertext """ Got this panda plush toy for my daughters birthday, who loves it and takes it everywhere. Its soft and super cute, and its face has a friendly look. Its a …

[linux] seqeval安装报错

新建一个新的环境 然后安装&#xff1a; # 不能拷贝别人的环境再安mebert_wash的环境。有冲突。我需要重新安一个空的conda环境&#xff0c;再安装。 # conda create -n wash python3.10 ipykernel python -m pip install --upgrade setuptools python -m pip install --upgr…

函数-递归调用

目录 一、基本介绍 二、递归能解决什么问题&#xff1f; 三、递归案例 1、打印问题 2、阶乘问题 四、递归重要规则 五、课堂练习 1、斐波那契数 2、猴子吃桃问题 3、汉诺塔 一、基本介绍 1、简单地说&#xff1a;递归就是函数自己调用自己&#xff0c;每次调用时传入…

利用python自动化运维i脚本实现远程连接服务器并实现相应命令

目录 前言&#xff1a; 一.调用的python库介绍 二.在主机上安装好相应的库 2.1激活虚拟环境 三.代码实现以及解析 四.效果的实现 五.致谢 前言&#xff1a; 在当今快速发展的技术环境中&#xff0c;自动化运维已成为 IT 基础设施管理的关键组成部分。它不仅可以显著提…