SpringBoot统一功能处理

文章目录

  • [1. 拦截器](#1. 拦截器)
    • [1.1 拦截路径](#1.1 拦截路径)
    • [1.2 拦截器执行流程](#1.2 拦截器执行流程)
    • [1.3 自定义拦截器](#1.3 自定义拦截器)
    • [1.4 注册配置拦截器](#1.4 注册配置拦截器)
  • [2. 统一数据返回格式](#2. 统一数据返回格式)
  • [3. 统一异常处理](#3. 统一异常处理)
  • [4. 总结](#4. 总结)

1. 拦截器

拦截器是Spring框架提供的核心功能之一, 主要用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码

在拦截器当中,开发人员可以在应用程序中做一些通用性的操作, 比如通过拦截器来拦截前端发来的请求, 判断Session中是否有登录用户的信息. 如果有就可以放行, 如果没有就进行拦截.

拦截器的使用分两步:

  1. 自定义拦截器: 实现HandlerInterceptor接口,并重写其所有方法
  2. 注册配置拦截器: 实现WebMvcConfigurer接口,并重写addInterceptors方法

先简单介绍实现HandInterceptor接口要重写的方法:

  1. preHandle() :目标方法执行前执行, 返回true, 继续执行后续操作; 返回false, 中断后续操作.
  2. postHandle() : 目标方法执行后执行
  3. afterCompletion(): 视图渲染完毕后执行,最后执行(后端开发现在几乎不涉及视图, 暂不了解)

1.1 拦截路径

拦截路径是指我们定义的这个拦截器, 对哪些请求生效,

我们在注册配置拦截器的时候, 通过 addPathPatterns() 方法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求, 具体使用会在后文介绍

常见拦截路径配置

/* :一级路径

/** :任意级路径

/book/* :/book下的一级路径

/book/** :/book下的任意级路径

1.2 拦截器执行流程

添加拦截器后, 执行Controller的方法之前, 请求会先被拦截器拦截住. 执行 preHandle() 方法,这个方法需要返回一个布尔类型的值.

如果返回true, 就表示放行本次操作, 继续访问controller中的方法.

如果返回false,则不会放行(controller中的方法也不会执行).

controller当中的方法执行完毕后,再回过来执行 postHandle() 这个方法以及afterCompletion() 方法,执行完毕之后,最终给浏览器响应数据

1.3 自定义拦截器

java 复制代码
@Component
@Slf4j
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;
    }
}

注意要加上@Component交给Spring管理

http状态码401: Unauthorized

未经过认证. 指示身份验证是必需的, 没有提供身份验证或身份验证失败. 如果请求已经包含授权凭据,那么401状态码表示不接受这些凭据

1.4 注册配置拦截器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    //排除前端静态资源
    private List<String> excludePaths = Arrays.asList(
            "/user/login",
            "/**/*.js",
            "/**/*.css",
            "/**/*.png",
            "/**/*.html"
    );

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义拦截器对象
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") //设置拦截器拦截的请求路径(/**表示拦截所有请求)
                .excludePathPatterns(excludePaths); //设置拦截器排除拦截的路径
    }
}

.excludePathPatterns的参数可以是Sting, 可也意识List<String>

2. 统一数据返回格式

统一的数据返回格式使用 @ControllerAdvice 和ResponseBodyAdvice 的方式实现:

@ControllerAdvice 表示控制器通知类

实现 ResponseBodyAdvice 接口, 并在类上添加@ControllerAdvice 注解

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

beforeBodyWrite方法: 对response方法进行具体操作处理

java 复制代码
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return Result.success(body);
    }
}

大多数项目确实不再单纯依赖它来做统一的返回类型封装, 而是结合泛型封装作为主力,ResponseBodyAdvice 作为辅助,

Controller 返回类型基本都是我们自定义的Result<T>, ResponseBodyAdvice 角色 只做有条件的增强(加密、脱敏、加头),不做统一格式包装

如果用ResponseBodyAdvice返回结果为String类型, 还要使用SpringBoot内置提供的Jackson来实现信息的序列化

java 复制代码
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

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

3. 统一异常处理

统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的

@ControllerAdvice 表示控制器通知类 ;

@ExceptionHandler 是异常处理器 ;

两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件

类名, 方法名和返回值可以自定义, 重要的是注解

接口返回为数据时, 需要加 @ResponseBody 注解

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

java 复制代码
@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());
    }
}

当有多个异常通知时,匹配顺序为当前类及其子类向上依次匹配

4. 总结

  • 拦截器的实现主要分两部分: 1. 定义拦截器(实现HandlerInterceptor 接口) 2. 配置拦截器(实现WebMvcConfigurer接口)
  • 统一数据返回格式通过@ControllerAdvice + ResponseBodyAdvice 来实现
  • 统一异常处理使用@ControllerAdvice + @ExceptionHandler 来实现
相关推荐
逃逸线LOF34 分钟前
Ribbon负载均衡原理(Irule组件、源码分析)
后端·spring cloud·ribbon
砚底藏山河43 分钟前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
慢云智慧空间1 小时前
从设备智能到空间理解,空间智能终端解决方案商如何重新定义行业标准
后端
利威尔xu1 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
猿究院_夏燊1 小时前
MySQL 的四种隔离级别
java·数据库·mysql
薛定谔的猫-菜鸟程序员2 小时前
软件架构设计师开发资料大全(从入门到精通)
后端·python·系统架构·flask·架构设计师
码流子2 小时前
移动收费怎么做?手持终端+App+收费中心的架构实践
java·开发语言·架构
霸道流氓气质2 小时前
AI 流式对话与实时聊天平台:SSE 解码竞态、有状态会话隔离与背压取消的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
云创智城-yuncitys2 小时前
YunParking 4.0:JDK 17 与 Spring Boot 3.2 的停车云升级
java·开发语言·spring boot
文丑颜不良啊2 小时前
LeetCode1661~1680题目解析
java·leetcode