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 来实现
相关推荐
coder!mq7 小时前
说几个常见的语法糖?
java·开发语言·算法
gugucoding7 小时前
38. 【Java】Stream API(下):高级操作与性能
java·开发语言
心平气和量大福大8 小时前
android-实例-蒲公英-更新与安装-5-签名与生成APK
android·java·开发语言
952368 小时前
Spring-cloud配置中心
java·spring·spring cloud·微服务
ydd1001008 小时前
寻找两个正序数组的中位数 Java 题解,二分分割详解
java·开发语言
65岁退休Coder9 小时前
LangChain v1.3.4 笔记 - 07 补充:链式调用 LCEL
后端·python·langchain
卷无止境9 小时前
FastAPI 部署在 Nginx 后面到底该怎么配
后端·python
码流怪侠9 小时前
用 WiFi 信号数人头:howmanypeoplearearound 项目深度解析
后端·开源·github