Spring Boot 统一功能处理

  • 一、使用 Spring 拦截器来实现登录验证
    • 引入
    • 代码实现
      • 步骤1 自定义拦截器
      • 步骤2 添加拦截器并设置拦截规则
        • 拦截规则
      • 步骤3 创建前后端交互,用于测试
    • 拦截器原理
  • 二、统一异常处理
    • 引入
    • 代码实现
  • 三、统一数据返回格式
  • 四、为所有前端发送的 URL 地址都添加前缀 (了解)

一、使用 Spring 拦截器来实现登录验证

引入

一般来说,session 会话被我们用来作为用户的登录验证,以往我们会封装一个类,专门用于获取 session 中的用户,如果获取到了,说明用户已经登录了;反之,则没有登录。但即便封装一个类,在每个 Controller 类中,依然需要通过此封装好的类,写几行代码来继续验证用户是否已经登录了。所以,每个控制层都需要验证,这其实很不方便。

所以,就有了 Spring 拦截器的功能,它可以自定义我们想要拦截的页面。然而,我们仍然是需要 Servlet 为我们提供的 request 对象,因为它和 session 会话紧密相连。

代码实现

步骤1 自定义拦截器

① 创建一个类 (自定义拦截器)
② 让当前类实现 Spring 框架提供的 HandlerInterceptor 接口
③ 重写 preHandle 方法 (需要明确,这一步拦截器的逻辑是由我们自己实现的。)

创建 LoginInterceptor 类,作为登录拦截的作用:

// 自定义用户登录的拦截器(自定义拦截前端访问的逻辑)
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession httpSession = request.getSession(false);if (httpSession != null && httpSession.getAttribute("userinfo") != null) {// 表示用户已经登录return true;}// 代码走到这里,说明用户还未登录// 在这里可以实现自己的业务逻辑,比方说让用户再次跳转到登录页面response.sendRedirect("login.html");return false;}
}

对 preHandle 方法进行说明:

preHandle 方法的返回值是布尔类型的,如果最终我们的代码返回 true,表示判断通过,可以访问后面的接口(控制层);如果最终我们的代码返回 false,表示拦截器拦截成功,直接返回错误结果 (空白页面) 给前端。

实际上,这就是 AOP 的思想,拦截器在前端和后端中间插了一杠子,此时,前端访问并不直接与控制层打交道了,而是先和拦截器这个动态代理打交道,如果代理这一关过了,才能继续走后端的接口。

步骤2 添加拦截器并设置拦截规则

将步骤1 自定义的拦截器加入到框架的配置中,并且设置拦截规则。

① 创建一个类并标明 " @Configuration " 注解
② 让当前类实现 WebMvcConfigurer 接口
③ 重写 addInterceptors 方法,在方法中添加拦截规则

创建 AddConfiguration 类,添加一些拦截规则:

@Configuration
public class AddConfiguration implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**"). // 拦截所有 URLexcludePathPatterns("/login"). // 不拦截登录接口excludePathPatterns("/register"). // 不拦截注册接口excludePathPatterns("/login.html"). // 不拦截登录的静态页面excludePathPatterns("/**/*.js"). // 不拦截所有的 js 文件excludePathPatterns("/**/*.css"). // 不拦截所有的 css 文件excludePathPatterns("/**/*.png"); // 不拦截所有的 png 图片}
}

拦截规则

① addPathPatterns:表示需要拦截的 URL

② excludePathPatterns:表示不需要拦截的 URL

③ **:表示拦截所有方法

④ 拦截规则不仅可以拦截 URL,也可以拦截静态 html页面,CSS、JS 文件

步骤3 创建前后端交互,用于测试

创建 UserController 类,与前端交互:

@RestController
public class UserController {@RequestMapping("/login")public String login(HttpServletRequest request, String username, String password) {if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {if (username.equals("Jack") && password.equals("123")) {// 登录成功后,创建 session 会话,并往会话中放入 userinfo 对象HttpSession httpSession = request.getSession(true);httpSession.setAttribute("userinfo", "userinfo");return "<h1> 登录成功 </h1>";}}return "<h1> 登录失败 </h1>";}@RequestMapping("/index")public String index() {return "你好,index";}
}

创建 login.html,以作登录失败的跳转提示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>login 页面</title>
</head>
<body><h1>请重新登录</h1>
</body>
</html>

展示效果:

在我们未登录而访问 index 路径时,就会重定向至登录页面了。

当我们登录成功后,才能正确访问被拦截的页面:

拦截器原理

拦截器在前端和后端中间插了一杠子,此时,前端访问并不直接与控制层打交道了,而是先和拦截器这个动态代理打交道。拦截器就像高铁站的安检一样,只有前端通过了安检,才能与后端控制层打交道。

此外,一个项目中可以同时配置多个拦截器。比方说,一个拦截器作为登录验证;另一个拦截器用作前端传递来的非法参数校验。( 只是两个的拦截逻辑和拦截规则不同罢了 )

二、统一异常处理

引入

假设你在浏览网页的时候,出现了下面的页面,你会有什么心情?

显然,你的第一反应不是懵懵的,就是暴躁的,反正不是快乐的。

如果我们做一个项目,未考虑到服务器端代码的异常、用户的传参异常,或是其他异常,就会出现类似于上面的错误信息。上面的 " 500 " 状态码,显然就意味着服务器端出错了。因为用户并不是程序员,他们根本看不懂后端的错误提示,甚至连前端拿到 500 这样的错误信息,也不知道后端出现了什么问题。所以,前后端如何配合,才能够将异常以一种 " 可理解 " 的方式展现在大众面前,是一件非常有意义的事情。

代码实现

① 创建一个异常类
② 为类添加 " @RestControllerAdvice " / " @ControllerAdvice " 注解
③ 为方法上添加 " @ExceptionHandler(xxx.class) " 注解

创建 MyExceptionAdvice 类,在里面实现统一异常处理

@RestControllerAdvice
public class MyExceptionAdvice {@ExceptionHandler(Exception.class)public HashMap<String, Object> ExceptionAdvice(Exception e) {HashMap<String, Object> result = new HashMap<>();result.put("status", -1);result.put("data", null);result.put("message", "发现异常:" + e.getMessage()) ;System.out.println("异常出现的位置:");e.printStackTrace();return result;}@ExceptionHandler(NullPointerException.class)public HashMap<String, Object> nullPointExceptionAdvice(NullPointerException e) {HashMap<String, Object> result = new HashMap<>();result.put("status", -1);result.put("data", null);result.put("message", "空指针异常:" + e.getMessage());System.out.println("异常出现的位置:");e.printStackTrace(); // 给后端程序员观看出错的位置return result; // 将异常数据打包成 json 数据,交给前端}
}

创建 UserController 类,用于测试:

@RestController
public class UserController {@RequestMapping("/index")public String index() {return "你好,index";}@RequestMapping("/error_demo")public String error(){String result = null;result.equals("123"); // 空指针异常return null;}@RequestMapping("/error_demo2")public String error2(){int a = 10 / 0; // 算数异常return null;}
}

展示结果:json 数据

IDEA 控制台打印异常日志:

注意事项:

① 异常有很多种,算数异常、数组越界、空指针异常…我们不可能将所有异常全部列举出来,所以我们可以写所有异常的父类 Exception.

② 当指定的异常和 Exception 异常同时存在,那么异常类会先执行前者。就如上面的例子,我已经指定了空指针异常,也写出了 Exception 异常,但最终执行的是空指针异常。

③ 后端不但要保证能够将简化后的异常信息返回给前端,也要知道异常的问题出现在了什么地方。所以,后端使用 " e.printStackTrace(); " 就能够在 IDEA 上很好地观察异常出现位置。

④ 前端一般只需要拿到约定的错误数据即可 (常见的 json 数据),至于怎么将错误信息展示给用户看,是前端的职责。遇到异常,怎么修改,这才是后端的事情。

三、统一数据返回格式

① 创建一个统一数据返回格式的类
② 为类添加 " @RestControllerAdvice " / " @ControllerAdvice " 注解
③ 让类实现 ResponseBodyAdvice 接口
④ 重写 supports、beforeBodyWrite 方法

创建 MyResponseAdvice 类,在里面构造统一返回对象

@RestControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice {/*** 如果返回 true, 表示返回数据之前对数据进行重写,也就会进入 beforeBodyWrite 方法* 如果返回 false,表示结果不进行任何处理,直接返回*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 构造统一返回对象HashMap<String, Object> result = new HashMap<>();result.put("status", 1);result.put("data", body); // 将原先放在 body 数据的内容,现在放在了 data 之中result.put("message", "");return result;}
}

创建 UserController 类,用于测试:

@RestController
public class UserController {@RequestMapping("/login2")public boolean login2(HttpServletRequest request, String username, String password) {if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {if (username.equals("Jack") && password.equals("123")) {HttpSession httpSession = request.getSession(true);httpSession.setAttribute("userinfo", "userinfo");return true;}}return false;}
}

展示结果:

注意事项:

统一数据返回格式的优点有很多,比如以下几个:

① 方便前端程序员更好的接收和解析后端数据接口返回的数据。
② 降低前端程序员和后端程序员的沟通成本,两者按照某个格式实现就行了,因为所有接口都是这样返回的。此外,更有利于项目统一数据的维护和修改。
③ 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。

但实际上,统一也就意味着不能个性化,如果前后端交互的数据是统一的,在某些特殊的场景下,不能够很好地解决、优化问题。所以,是否选择统一数据返回,还需要根据具体的业务场景决定。

四、为所有前端发送的 URL 地址都添加前缀 (了解)

@Configuration
public class AddConfiguration implements WebMvcConfigurer {// 为所有前端发送的 URL 地址都添加前缀@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("app", c -> true);}
}

Spring Boot 统一功能处理相关推荐

  1. Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)

    文章目录 1. 统一用户登录权限效验 1.1 最初用户登录权限效验 1.2 Spring AOP 统一用户登录验证 1.3 Spring 拦截器 1.4 练习:登录拦截器 1.5 拦截器实现原理 1. ...

  2. Spring Boot统一异常处理实践

    Spring Boot统一异常处理实践 参考文章: (1)Spring Boot统一异常处理实践 (2)https://www.cnblogs.com/fundebug/p/springboot-ex ...

  3. Spring AOP统一功能处理(切面、切点、连接点、通知)

    目录 一. AOP的一些前置知识 1.1什么是Aop 1.2 AOP的作用 1.3AOP基础组成 二.SpringAOP的实现 2.1添加SpringAOP框架支持 2.2定义切面(Aspect) 2 ...

  4. Spring Boot统一异常处理的拦截指南

    通常我们在Spring Boot中设置的统一异常处理只能处理Controller抛出的异常.有些请求还没到Controller就出异常了,而这些异常不能被统一异常捕获,例如Servlet容器的某些异常 ...

  5. Spring Boot统一日志框架

    在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析.在 Java 领域里存在着多种日志框架,如 JCL.SLF4J.Jboss-logging.jUL.log4j. ...

  6. 12. Spring Boot统一日志框架

    在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析.在 Java 领域里存在着多种日志框架,如 JCL.SLF4J.Jboss-logging.jUL.log4j. ...

  7. [Spring Boot核心功能]1. SpringApplication 启动引导类(1)

    SpringApplication 是Spring Boot 提供的一个类,该类提供了一个便捷的方法引导Spring应用, 从应用的入口main() 方法启动应用, 主方法直接调用SpringAppl ...

  8. start.sh spring boot 统一启动脚本 支持jenkins 等工具使用

    脚本根据应用包名判断是否有启动进程,如有启动进程则会将进程先kill掉 将脚本保存为restart.sh # java 安装路径 java_home="/opt/tools/jdk1.8.0 ...

  9. Spring boot统一日志记录

    统一日志记录 开发的时候 用到都是一个框架 很多的框架(slf4j+logback): Spring(commons-logging) Hibernate(jboss-logging) MyBatis ...

最新文章

  1. 比特币现金(BCH)和比特币(BTC)之争到底在争些什么?
  2. HID报表描述符(目前最全的解析,也是USB最复杂的描述符)
  3. GraphQL:和EntityFramework更配哦
  4. Struts2初始化流程及源码分析
  5. 天气预报的Ajax效果
  6. c语言汇编混编,c语言与汇编混编写法
  7. arm poky linux,Solved: Re: arm-poky-linux - NXP Community
  8. Maven和Eclipse整合
  9. netty java_GitHub - leihuazhe/Java11-Netty-Demo: 基于Java11 构建的 netty 服务端客户端 模块化例子...
  10. 计算机系制作网页,613331-付军科-计算机系网页设计与制作实训报告【荐】.doc
  11. pubmed显示服务器不稳定,你的pubmed又不能显示影响因子了,因为 ……
  12. vscode设置中文流程
  13. MVT 之 M——模型
  14. php 完全前后端分离使用jwt,前后端分离,在 angular 8 中利用 JWT 进行身份认证
  15. 顺序表如何插入元素? 看这里!!
  16. Infor CloudSuite Industrial (SyteLine) 工序外协基本流程
  17. 机械指令 对应 汇编指令
  18. CDLinux U盘启动教程
  19. 【图像分割】基于脉冲耦合神经网络实现图像分割附matlab代码
  20. 哥特巴赫猜想 尾递归 湘潭孕妇之后的自我检讨

热门文章

  1. 关于Mysql服务无法启动的问题
  2. 华为(huawei)园区网的常规部署配置示例
  3. 虚数的现实、物理意义是什么?
  4. 计算机面试工作计划,信息技术部面试自我介绍
  5. 反诈中心拦截网站域名措施与申诉方法
  6. python小作业8代码(列表的遍历与嵌套)
  7. iOS编程------SQLite / 数据库
  8. ng-model、ng-bind、ng-value使用区别
  9. 面试了几十家软件测试公司全是这个“套路”
  10. 应用回归分析(知识点整理)(四)——SPSS处理自相关(序列相关)