• 【SpringBoot】统一功能处理


    目录

    🎃1 拦截器

    🎀1.1 拦截器的代码实现

    🎨1.2 拦截器的实现原理

    🧶2 拦截器应用——登录验证

    🦺3 异常统一处理

    🎭4 统一数据返回格式

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

    🧣4.2 统一返回对象

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


    本篇文章介绍 Spring Boot 的统一功能处理模块,也就是 AOP 的实战环节。

    1 拦截器

    没有登录的情况下,会跳转到登录页面。

    1.1 拦截器的代码实现

    Spring 提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

    1. 创建自定义拦截器,实现 HandlerInterceptor 接口,重写 preHandle 方法。(执行具体方法之前的预处理)。

    2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中。

    1. package com.example.demo.configuration;
    2. import com.example.demo.common.AppVar;
    3. import org.springframework.stereotype.Component;
    4. import org.springframework.web.servlet.HandlerInterceptor;
    5. import javax.servlet.http.HttpServletRequest;
    6. import javax.servlet.http.HttpServletResponse;
    7. import javax.servlet.http.HttpSession;
    8. /**
    9. * 自定义拦截器
    10. */
    11. @Component
    12. public class UserInterceptor implements HandlerInterceptor {
    13. /**
    14. * 返回 true -> 拦截器验证成功,继续执行后续的方法
    15. * false -> 拦截器验证失败,不会执行后续的目标方法
    16. * @param request
    17. * @param response
    18. * @param handler
    19. * @return
    20. * @throws
    21. */
    22. @Override
    23. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    24. System.out.println("do UserInterceptor"); // 拦截时候会打印
    25. // 业务方法
    26. HttpSession session = request.getSession(false);
    27. if(session != null &&
    28. session.getAttribute(AppVar.SESSION_KEY) != null){
    29. // 用户已经登录了
    30. return true; // 继续执行后续流程
    31. }
    32. // 未登录的情况,跳转到 百度
    33. response.sendRedirect("https://www.baidu.com");
    34. return false;
    35. }
    36. }
    1. package com.example.demo.common;
    2. /**
    3. * 全局变量
    4. */
    5. public class AppVar {
    6. // Session Key
    7. public static final String SESSION_KEY = "SESSION_KEY";
    8. }

    1. package com.example.demo.configuration;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    6. /**
    7. * 将自定义拦截器加入到系统配置
    8. * 有两种写法
    9. * 一是 new 一个 UserInterceptor 对象
    10. * 二是 注入的方式
    11. */
    12. @Configuration
    13. public class AppConfig implements WebMvcConfigurer {
    14. @Autowired
    15. private UserInterceptor userInterceptor;
    16. @Override
    17. public void addInterceptors(InterceptorRegistry registry) {
    18. // registry.addInterceptor(new UserInterceptor());
    19. registry.addInterceptor(userInterceptor)
    20. .addPathPatterns("/**") // 拦截所有请求
    21. .excludePathPatterns("/user/reg") // 登录页面不拦截
    22. .excludePathPatterns("/user/login") // 注册页面不拦截
    23. ;
    24. }
    25. }

     * 一级路由,** 所有路由。

    /user 就是一级路由,/user/reg 是二级路由。

    addPathPatterns 表示需要拦截的 URL,** 表示拦截所有方法

    excludePathPattern 表示需要排除的 URL 

    1. package com.example.demo.controller;
    2. import org.springframework.web.bind.annotation.RequestMapping;
    3. import org.springframework.web.bind.annotation.RestController;
    4. @RestController
    5. @RequestMapping("/user")
    6. public class UserController {
    7. @RequestMapping("/getuser")
    8. public String getUser(){
    9. System.out.println("do getUser()");
    10. return "getuser";
    11. }
    12. @RequestMapping("/reg")
    13. public String reg(){
    14. System.out.println("do reg()");
    15. return "reg";
    16. }
    17. @RequestMapping("/login")
    18. public String getlogin(){
    19. System.out.println("do login()");
    20. return "login";
    21. }
    22. }

    输出:

    do UserInterceptor
    do reg()
    do login()

    1.2 拦截器的实现原理

    正常情况下的调用顺序:

    有了拦截器之后,在调用 controller 之前,会执行拦截器,如果为 true,则继续执行后续程序,如果为 false,则跳转相关页面。

    2 拦截器应用——登录验证

    1. package com.example.demo.configuration;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    6. /**
    7. * 将自定义拦截器加入到系统配置
    8. * 有两种写法
    9. * 一是 new 一个 UserInterceptor 对象
    10. * 二是 注入的方式
    11. */
    12. @Configuration
    13. public class AppConfig implements WebMvcConfigurer {
    14. @Autowired
    15. private UserInterceptor userInterceptor;
    16. @Override
    17. public void addInterceptors(InterceptorRegistry registry) {
    18. // registry.addInterceptor(new UserInterceptor());
    19. registry.addInterceptor(userInterceptor)
    20. .addPathPatterns("/**") // 拦截所有请求
    21. .excludePathPatterns("/user/reg") // 登录页面不拦截
    22. .excludePathPatterns("/reg.html")
    23. .excludePathPatterns("/login.html")
    24. .excludePathPatterns("/css/**")
    25. .excludePathPatterns("/editor.md/**")
    26. .excludePathPatterns("/img/**")
    27. .excludePathPatterns("/js/**")
    28. .excludePathPatterns("/user/login") // 注册页面不拦截
    29. .excludePathPatterns("/image/**") // image 文件下所有的图片格式拦截
    30. ;
    31. }
    32. }

    除了注册页面、登录页面之外,其余页面都会跳转到百度。

    3 异常统一处理

    1. @RequestMapping("/reg")
    2. public String reg(){
    3. System.out.println("do reg()");
    4. Object obj = null;
    5. System.out.println(obj.hashCode());
    6. return "reg";
    7. }

    如果每个页面都出现异常,可不可以统一处理呢?

    出现的所有异常按照统一的格式返回: 

    1. package com.example.demo.common;
    2. import lombok.Data;
    3. /**
    4. * 统一返回对象
    5. */
    6. @Data
    7. public class ResultAjax {
    8. private int code; // 状态码
    9. private String msg; // 状态码的描述信息
    10. private Object data; // 返回数据
    11. }

    异常统一处理时,需要两个注解。一个是 @ControllerAdvice / @RestControllerAdvice ,另一个是 @ExceptionHandler(Exception.class) 统一返回对象。

    1. package com.example.demo.configuration;
    2. import com.example.demo.common.ResultAjax;
    3. import org.springframework.web.bind.annotation.ExceptionHandler;
    4. import org.springframework.web.bind.annotation.RestControllerAdvice;
    5. @RestControllerAdvice
    6. public class ExceptionAdvice {
    7. @ExceptionHandler(NullPointerException.class)
    8. public ResultAjax doNullPointerException(NullPointerException e){
    9. ResultAjax resultAjax = new ResultAjax();
    10. resultAjax.setCode(-1);
    11. resultAjax.setMsg("空指针异常:"+e.getMessage());
    12. resultAjax.setData(null);
    13. return resultAjax;
    14. }
    15. }

    再举一个算术异常的例子:

    由于所有的异常都继承自 Exception,所以  @ExceptionHandler(Exception.class) 里面的异常类不用写那么详细也可以:

    1. @RequestMapping("/login")
    2. public String login(){
    3. System.out.println("do login()");
    4. int num = 10 / 0;
    5. return "login";
    6. }
    1. @ExceptionHandler(Exception.class)
    2. public ResultAjax doException(Exception e){
    3. ResultAjax resultAjax = new ResultAjax();
    4. resultAjax.setCode(-1);
    5. resultAjax.setMsg("异常:"+e.getMessage());
    6. resultAjax.setData(null);
    7. return resultAjax;
    8. }

    4 统一数据返回格式

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

    统⼀数据返回格式的优点有很多,⽐如以下⼏个:

    1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。

    2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就⾏了,因为所有接⼝都是这样返回 的。

    3. 有利于项⽬统⼀数据的维护和修改。

    4. 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容。

    4.2 统一返回对象

    对 ResultAjax 这个类进行改造,添加两种方法,一是成功之后返回的数据,二是失败之后返回的数据。

    1. package com.example.demo.common;
    2. import lombok.Data;
    3. /**
    4. * 统一返回对象
    5. */
    6. @Data
    7. public class ResultAjax {
    8. private int code; // 状态码
    9. private String msg; // 状态码的描述信息
    10. private Object data; // 返回数据
    11. /**
    12. * 成功时返回
    13. * @param data
    14. * @return
    15. */
    16. public static ResultAjax success(Object data){
    17. ResultAjax resultAjax = new ResultAjax();
    18. resultAjax.setCode(200);
    19. resultAjax.setMsg("");
    20. resultAjax.setData(data);
    21. return resultAjax;
    22. }
    23. public static ResultAjax success(String msg, Object data){
    24. ResultAjax resultAjax = new ResultAjax();
    25. resultAjax.setCode(200);
    26. resultAjax.setMsg(msg);
    27. resultAjax.setData(data);
    28. return resultAjax;
    29. }
    30. public static ResultAjax fail(int code, String msg){
    31. ResultAjax resultAjax = new ResultAjax();
    32. resultAjax.setCode(code);
    33. resultAjax.setMsg(msg);
    34. resultAjax.setData(null);
    35. return resultAjax;
    36. }
    37. public static ResultAjax fail(int code, String msg, Object data){
    38. ResultAjax resultAjax = new ResultAjax();
    39. resultAjax.setCode(code);
    40. resultAjax.setMsg(msg);
    41. resultAjax.setData(data);
    42. return resultAjax;
    43. }
    44. }
    1. package com.example.demo.controller;
    2. import com.example.demo.common.ResultAjax;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. import org.springframework.web.bind.annotation.RestController;
    5. @RestController
    6. @RequestMapping("/user")
    7. public class UserController {
    8. @RequestMapping("/getuser")
    9. public ResultAjax getUser(){
    10. System.out.println("do getUser()");
    11. return ResultAjax.success("getuser");
    12. }
    13. @RequestMapping("/reg")
    14. public ResultAjax reg(){
    15. System.out.println("do reg()");
    16. return ResultAjax.success("reg");
    17. }
    18. @RequestMapping("/login")
    19. public ResultAjax login(){
    20. System.out.println("do login()");
    21. return ResultAjax.success("login");
    22. }
    23. }

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

    如果就是有人不按要求返回 ResultAjax 这一格式呢?比如下面这样:

    1. @RequestMapping("getnum")
    2. public int getNum(){
    3. System.out.println("getNum()");
    4. return 1;
    5. }

    这时就可以对返回的数据进行统一处理,这是强制执行的。

    使用:

    1. @ControllerAdvice 

    2. 实现 ResponseBodyAdvice 接口,并重写它的两个方法,supports 必须返回 true,beforeBodyWrite 方法中进行重新判断和重写操作。

    1. package com.example.demo.configuration;
    2. import com.example.demo.common.ResultAjax;
    3. import org.springframework.core.MethodParameter;
    4. import org.springframework.http.MediaType;
    5. import org.springframework.http.server.ServerHttpRequest;
    6. import org.springframework.http.server.ServerHttpResponse;
    7. import org.springframework.web.bind.annotation.ControllerAdvice;
    8. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    9. @ControllerAdvice
    10. public class ResponAdvice implements ResponseBodyAdvice {
    11. @Override
    12. public boolean supports(MethodParameter returnType, Class converterType) {
    13. return true;
    14. }
    15. @Override
    16. public Object beforeBodyWrite(Object body, MethodParameter returnType,
    17. MediaType selectedContentType,
    18. Class selectedConverterType,
    19. ServerHttpRequest request,
    20. ServerHttpResponse response) {
    21. // 已经包装好的对象
    22. // body 是 ResultAjax 的格式
    23. if(body instanceof ResultAjax){
    24. return body;
    25. }
    26. // 对字符串进行判断和处理
    27. // 重新封装成 ResultAjax 的格式
    28. return ResultAjax.success(body);
    29. }
    30. }

    1. @RequestMapping("/getstr")
    2. public String getStr(){
    3. System.out.println("getStr()");
    4. return "whoooooo~~";
    5. }

     如果返回的是 String,而不是 int 类型,会报错。在把 String 转化成 json格式的时候报错了。所以对于返回类型是 String 的话,需要单独处理。不使用 String 解析引擎,而是手动转成 json。

    1. package com.example.demo.configuration;
    2. import com.example.demo.common.ResultAjax;
    3. import com.fasterxml.jackson.core.JsonProcessingException;
    4. import com.fasterxml.jackson.databind.ObjectMapper;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.core.MethodParameter;
    7. import org.springframework.http.MediaType;
    8. import org.springframework.http.server.ServerHttpRequest;
    9. import org.springframework.http.server.ServerHttpResponse;
    10. import org.springframework.web.bind.annotation.ControllerAdvice;
    11. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    12. @ControllerAdvice
    13. public class ResponAdvice implements ResponseBodyAdvice {
    14. // springboot 框架自动注入
    15. @Autowired
    16. private ObjectMapper objectMapper;
    17. @Override
    18. public boolean supports(MethodParameter returnType, Class converterType) {
    19. return true;
    20. }
    21. @Override
    22. public Object beforeBodyWrite(Object body, MethodParameter returnType,
    23. MediaType selectedContentType,
    24. Class selectedConverterType,
    25. ServerHttpRequest request,
    26. ServerHttpResponse response) {
    27. // 已经包装好的对象
    28. // body 是 ResultAjax 的格式
    29. if(body instanceof ResultAjax){
    30. return body;
    31. }
    32. // 对字符串进行判断和处理
    33. // 手动转换成 json 格式
    34. if(body instanceof String){
    35. ResultAjax resultAjax = ResultAjax.success(body);
    36. try {
    37. return objectMapper.writeValueAsString(resultAjax);
    38. } catch (JsonProcessingException e) {
    39. e.printStackTrace();
    40. }
    41. }
    42. return ResultAjax.success(body);
    43. }
    44. }

    如果返回的是对象呢? 

    1. @RequestMapping("/usermsg")
    2. public User usermsg(){
    3. User user = new User();
    4. user.setId(263);
    5. user.setName("柳飘飘");
    6. user.setPassword("96134");
    7. return user;
    8. }


  • 相关阅读:
    Linux ❀ 进程出现process information unavailable时的消除方法
    高空抛物检测方案设计(使用SOM进行轨迹分类)
    vulnhub DriftingBlues: 6
    CVE-2023-32315:Openfire管理控制台身份验证绕过到RCE的复现
    基于VTD自带的场景 进行场景搭建
    linux系统Jenkins工具介绍
    基于python的pulp库使用,从基础模型到复杂模型,从一维变量到二维变量
    matlab相机标定求得相机内参
    Jvm上如何运行其他语言?JSR223规范最详细讲解
    【代码源每日一题】饿饿 饭饭「二分答案」
  • 原文地址:https://blog.csdn.net/qq_41233305/article/details/132639139