【SpringBoot】统一功能处理

目录

一、什么是统一功能处理

二、统一用户登录权限验证

[2.1 定义拦截器](#2.1 定义拦截器)

[2.2 制定拦截规则](#2.2 制定拦截规则)

[2.3 创建请求](#2.3 创建请求)

[2.4 拦截器实现原理](#2.4 拦截器实现原理)

三、统一异常处理

四、统一数据格式返回


一、什么是统一功能处理

SpringBoot 统一功能处理:

  • 定义:指在SpringBoot应用中,通过一些统一的处理机制,对具有共性的功能进行集中管理,以提高代码的可维护性和开发效率。
  • 应用范围:通常包括统一异常处理、统一数据返回格式、统一用户认证与授权等。

二、统一用户登录权限验证

2.1 定义拦截器

在 SpringMVC 中 HandlerInterceptor 接口是一种用于拦截 Http 请求、响应的机制,它允许开发者在访问控制器(Controller )之前加入一些自定义的规范,如拦截器中设置了 session ,当 Controller 请求成功时返回 true 则进行后续相关操作返回 false则结束。

首先在项目中有以下类:

  • ApplicationValue类 自定义的 session
  • UserInterceptor类 自定义拦截器
  • ApplicationConfig类 自定义拦截规范
  • UserController类 自定义请求

ApplicationValue:

复制代码
/**
 * 全局变量
 * */
public class ApplicationValue {
    //自定义session key
    public static final String SESSION_KEY = "SESSION_KEY";
}

UserInterceptor:

复制代码
/**
 * 自定义拦截器
 */
@Component
public class UserInterceptor implements HandlerInterceptor {
    /**
     *  * 返回true代表拦截器验证成功继续执行后续代码
     *  * 返回false代表拦截器验证失败不执行后续代码
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    //前置通知
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器开始执行");
        //业务方法
        HttpSession session = request.getSession(false);
        if(session!=null && session.getAttribute(ApplicationValue.SESSION_KEY) != null) {
            //登录成功
            return true;
        }
        return false;
    }
}

preHandle 方法是 UserInterceptor 接口底下的一个请求达到控制器之间(Controller )执行的一个方法。类似于 Spring AOP 中的前置通知。当请求满足拦截规则即 preHandle 方法返回 true 时则放行该请求,返回 false 则拦截该请求。

上述获取 session 时,在 getSession 方法中加入一个 false 是为了当获取的 session 不存在时不创建一个新的会话而是返回一个 null。


2.2 制定拦截规则

WebMvcConfigurer 是 Spring Framework 提供的一个接口,它可以使开发者在 Spring MVC 中不使用 xml 文件来约束规则,而是通过 java 代码来扩展 SpringMVC 相关操作。如制定自定义对某些 Web请求的拦截规则等。

ApplicationConfig:

复制代码
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
    //将拦截器注入进来
    @Autowired
    private UserInterceptor userInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //指定拦截规则
        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**") //拦截所有请求
                .excludePathPatterns("/user/getuser") //排除拦截某个请求
                ;
    }
}

InterceptorRegistry 是 Spring Framework 中的一个制定和管理拦截器的一个类。通过它底下的方法能放行或拦截某些请求。如 addPathPatterns 方法为拦截所有请求,excludePathPatterns 为排除拦截某个请求。


2.3 创建请求

UserController:

复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/getuser")
    public String getUser() {
        System.out.println("user访问成功");
        return "hello user";
    }

    @RequestMapping("/login")
    public String login() {
        System.out.println("登录成功");
        return "welcome login";
    }
}

上述代码中 @RequestMapping 为路由选择,它是 SpringMVC 中将 Http请求映射到某一个类或方法上的注解。

postman 测试,在浏览器或第三方软件中输入 localhost:8080/user/getuser 就能访问到上面制定的请求。

编译器输出:

此外,当你需要放行所有图片格式的数据时,图片我们知道有 png 、jpg 、jpeg等类型的,我们不可能一个个的类型进行放行,此时可以将这些类型的图片放入一个文件夹,只需要放行这一个文件夹即可。


2.4 拦截器底层原理

通过日常观察,在控制台打印信息中所有的 Controller 代码都会执行 DispatcherServlet 中的 doDispatch 调度方法,DispatcherServlet是前端控制器。

在 SpringBoot 中,当一个 Http 请求到达Spring MVC 中时,DispatcherServlet 作为前端控制器,负责接收并处理这个请求,在这个过程中会用到 doDispatch调度方法。

doDispatch 方法主要是负责调度和处理 Http请求,

拦截器底层主要代码:

复制代码
//调用预处理
 if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {
 return;
 }
 // 执⾏ Controller 中的业务
 mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
 if (asyncManager.isConcurrentHandlingStarted()) {
 return;
}

开始执⾏ Controller 之前,会先调⽤ 预处理⽅法 applyPreHandle:

复制代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse res
ponse) throws Exception {
 for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex
= i++) {
 // 获取项⽬中使⽤的拦截器 HandlerInterceptor
 HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
torList.get(i);
 if (!interceptor.preHandle(request, response, this.handler)) {
 this.triggerAfterCompletion(request, response, (Exception)null
);
 return false;
 }
 }
 return true;
}

在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执⾏拦截器中
的 preHandle ⽅法。


三、统一异常处理

当访问一个不存在的请求时,浏览器会出现一大片英文表示访问错误,并显示错误状态码为500,如下所示:

复制代码
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/reg")
    public String reg(){
        Object obj = null;
        System.out.println(obj.hashCode());
        return "注册成功";
    }
}

为了解决上述问题,可以使用 @ControllerAdvice 和 @ExceptionAdvice 这两个注解自定义设置某些异常对应返回的类型格式。如上述的空指针异常浏览器显示一大串英文字母,而通过这两个注解进行自定义设置一个 Ajax 格式,浏览器会显示一个状态码、异常提示、数据,让人看起来比较简洁,当然你也可以设置其他格式。

@ControllerAdvice 注解是 SpringFramework 中的一个注解,它是一个控制器通知类,主要的作用是用于定义全局异常处理器,以及用于全局数据绑定或全局数据格式化的逻辑。它提供了一种集中化的方式来处理跨多个控制器的通用逻辑,比如异常处理、数据预处理和后处理等。

@ExceptionAdvice 注解也是 SpringFramework 中的一个注解用于定义全局异常处理逻辑。它的主要功能是捕获特定类型的异常,并返回相应的响应数据,从而避免在每个控制器中重复编写异常处理代码。

ExceptionAdvice:

复制代码
@RestControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(NullPointerException.class)//统一访问对象
    public RequestAjax doNullPointerException (NullPointerException e) {
        RequestAjax requestAjax = new RequestAjax();
        requestAjax.setCode(-1);
        requestAjax.setMas("空指针异常:" + e.getMessage());
        requestAjax.setData(null);
        return requestAjax;
    }
}

RequestAjax:

复制代码
@Data
public class RequestAjax {
    private int code; //状态码
    private String mas; // 状态码描述
    private Object data; // 返回数据
}

结果:

以上是针对单个空指针异常进行的异常处理,异常的种类是非常多的如空指针异常、栈溢出异常等。假设一个程序中有多个异常都按照空指针异常来处理是不行的,因此改为以下代码使得无论是什么异常都会报出如异常:XXX,相比一大面英文字母好很多。

复制代码
    @ExceptionHandler(Exception.class)//统一访问对象
    public RequestAjax doException (Exception e) {
        RequestAjax requestAjax = new RequestAjax();
        requestAjax.setCode(-1);
        requestAjax.setMas("异常:" + e.getMessage());
        requestAjax.setData(null);
        return requestAjax;
    }

四、统一数据格式返回

ResponseBodyAdvice用途:

ResponseBodyAdvice 是 Spring 4.1 引入的一个接口,用于在 Controller 方法返回响应体之前,对响应体的内容进行处理。它允许开发者拦截 Controller 方法的返回值,并对返回值进行修改、增强或定制化。具体来说,ResponseBodyAdvice有以下几个主要用途:

  1. 统一处理返回值结构:

    • 可以对Controller方法的返回值进行统一的封装,例如,将所有API接口的返回值封装成统一的JSON结构,包含状态码、消息和数据等字段。
    • 便于前端统一处理不同接口的响应数据,提高开发效率。
  2. 数据加密或压缩:

    • 可以在响应体写入之前,对返回值进行加密或压缩处理,提高数据的安全性或传输效率。
  3. 添加额外信息:

    • 可以在返回值中添加一些额外的信息,例如时间戳、签名等,满足特定的业务需求。

则有以下代码:

复制代码
    @RequestMapping("/getuser")
    public String getUser() {
        System.out.println("user访问成功");
        return "hello user";
    }

@ControllerAdvice
public class RespondAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 当supports为true时执行后续操作,否则不执行
     * @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) {
       if (body instanceof RequestAjax) {
           return body;
       }
       if (body instanceof String) {
           RequestAjax requestAjax = RequestAjax.succ(body);
           try {
               return objectMapper.writeValueAsString(requestAjax);
           } catch (JsonProcessingException e) {
               e.printStackTrace();
           }
       }
        return RequestAjax.succ(body);
    }
}
相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
小羊没烦恼!4 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
俊昭喜喜里4 天前
java中的继承和多态的区别
java
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
譕痕4 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖4 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时4 天前
01-06-A-JVM排查实战详解
java·jvm