我们在使用 Spring Boot 做 web api 开发时,会遇到这几种场景:
- 不是每一个接口都可以随意访问的,需要有安全检验和权限控制;
- 对请求的接口做统一处理:
- 输入数据的统一处理:比如对参数的校验等
- 输出数据的统一处理:比如格式化等
- 对请求接口进行统计:
- 请求计数
- 日志打印等
以上的操作都涉及到在请求到达 server 后,分别调用相应 controller 相关方法的前、中、后作出相应的更改。
这时,拦截器 就派上用场了。
1. 拦截器(Interceptor) 与 过滤器 (Filter)
在描述 拦截器 前,我们先来说下拦截器与过滤器。因为这两个有些相似。
Spring 的拦截器与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 编程思想的提现,都能实现权限检查、日志记录等。不同的是:
- 使用范围不同:Filter 是 Servlet 规范规定的,只能用于 Web 程序中。而拦截器既可用于 Web 程序,也可用于 Application、Swing 程序中。
- 规范不同:Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring 容器内的,是 Spring 框架支持的。
- 使用资源不同:同其他的代码块一样,拦截器也是一个 Spring 的组件,归 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IoC 注入到拦截器既可;而 Filter 则不能。
- 深度不同: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<>();
}
}
源码: