010 Spring Boot 拦截器

我们在使用 Spring Boot 做 web api 开发时,会遇到这几种场景:

  1. 不是每一个接口都可以随意访问的,需要有安全检验和权限控制;
  2. 对请求的接口做统一处理:
    • 输入数据的统一处理:比如对参数的校验等
    • 输出数据的统一处理:比如格式化等
  3. 对请求接口进行统计:
    • 请求计数
    • 日志打印等

以上的操作都涉及到在请求到达 server 后,分别调用相应 controller 相关方法的前、中、后作出相应的更改。

这时,拦截器 就派上用场了。

1. 拦截器(Interceptor) 与 过滤器 (Filter)

在描述 拦截器 前,我们先来说下拦截器与过滤器。因为这两个有些相似。

Spring 的拦截器与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 编程思想的提现,都能实现权限检查、日志记录等。不同的是:

  1. 使用范围不同:Filter 是 Servlet 规范规定的,只能用于 Web 程序中。而拦截器既可用于 Web 程序,也可用于 Application、Swing 程序中。
  2. 规范不同:Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring 容器内的,是 Spring 框架支持的。
  3. 使用资源不同:同其他的代码块一样,拦截器也是一个 Spring 的组件,归 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IoC 注入到拦截器既可;而 Filter 则不能。
  4. 深度不同:Filter 在只在 Servlet 前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在 Spring 构架的程序中,要优先使用拦截器。

2. Spring Boot 使用拦截器

2.1 定义拦截器

在 Spring Boot 中定义拦截器十分的简单,只需要创建一个拦截器类,并实现 HandlerInterceptor 接口即可。

HandlerInterceptor 接口中定义以下 3 个方法,如下表:

返回值类型 方法声明 描述
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。
package org.example.frame.interceptor;

import org.example.frame.exception.JDZException;
import org.example.frame.util.RtCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("==========preHandle================");
        //return HandlerInterceptor.super.preHandle(request, response, handler);
        throw new JDZException(RtCode.ERR_NEED_TOKEN);
        //return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("==========postHandle================");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("==========afterCompletion================");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2.2 配置拦截器

package org.example.frame.config;

import org.example.frame.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class JDZInterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .excludePathPatterns(ApiConfig.notNeedCheckToken);

    }
}

说明:

  • addPathPatterns:该方法用于指定拦截路径,例如拦截路径为“/**”,表示拦截所有请求,包括对静态资源的请求。
  • excludePathPatterns:该方法用于排除拦截路径,即指定不需要被拦截器拦截的请求。

2.3 测试

package org.example.controller;

import org.example.frame.util.RtData;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @RequestMapping("/")
    public RtData<Object> index() {
        return new RtData<>();
    }
}

源码:

发表评论