一、概述
什么是拦截器?
是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制方法的执行。
到底是干啥用的?
拦截请求用的,在指定的方法调用前后,执行在拦截器中编写的程序
比如:我们做了一个页面,我们希望在用户登录之后才能访问后面的页面,但是他们可以直接在地址栏中输入 /接口 去访问后面的页面。
所以我们要使用拦截器,在用户直接访问了接口之后,在程序的方法执行前拦截住,检查一下有没有好好的登陆再决定方法是否要执行。
操作步骤
1、定义拦截器
实现HandlerInterceptor接口,并重写其中的所有方法
其中有三个方法需要重写
- preHandle:在目标方法(接口)执行前拦截住,先执行其中的逻辑后决定是否放行目标方法。返回值 true放行,false不放行
- postHandle:在目标方法(接口)执行后拦截住,执行其中的逻辑
- afterCompletion:在试图渲染完毕后拦截住,执行其中的逻辑
你可能会疑问,里面什么数据都没有,我们之前是通过参数声明的方式直接拿到,但是在重写的这个方法中并没有。那怎么在里面验证前后端的数据呢?没错,这是因为数据直接被包含在了这方法里的参数中
- HttpServletRequest:代表请求,所有的请求数据都在request对象中
- HttpServletRequest:代表响应,所有的响应数据都在response数据中
而且还在拦截器上加上@Component注解,那么就代表就要交给了IOC容器管理了,此时就变成了一个bean对象。
2、注册拦截器
只有在这里面引入刚才写完的拦截器后,拦截器才会生效。重写其中的addIntercepter方法。顾名思义,就是添加那些拦截器。
注:既然是拦截器,那么它就能拦截所有的接口。一定要注意不能让它拦截登录接口和注册接口,不然要访问数据接口就要登录生成的token,要登陆的token就要访问登录接口,由于登录接口也被拦截了,那么访问登录接口就要登录生成的token,要登录的token就要访问登录接口......开启了无限套娃。因此要使用excludePathPatterns方法排除不需要拦截的接口。
二、拦截详解
拦截路径
执行流程
当我们打开浏览器来访问部署在web服务器上的web应用时,此时过滤器Filter就会拦截到这次请求,然后依次执行 放行前操作,放行,放行后操作。之后就会进入springboot程序 。
Tomcat服务器是一个servlet容器,只识别servlet程序,不识别controller程序。因此Spring的web环境提供了一个前端控制器DispatcherServlet,请求会先到这里面,DispatcherServlet又会转给Controller,这个过程中会遇到拦截器Interceptor,以此执行preHandle、postHandle、afterCompletion。反过来响应也是如此
过滤器与拦截器的区别
- 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerIntercetor接口
- 拦截范围不同:过滤器Filter会拦截所有的资源,而拦截器Interceptor会拦只会拦截spring中的资源
过滤器与拦截器我们只使用其中一个就行了,推荐使用拦截器
三、使用案例
登录校验
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取令牌
String token = request.getHeader("Authorization");
//验证token
try {
Map<String,Object> claims = JwtUtil.parseToken(token);
//将解析的token信息放入ThreadLocal
ThreadLocalUtil.set(claims);
//没有异常就放行
return true;
} catch (Exception e) {
//未登录,不放行
response.setStatus(401);
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清空ThreadLocal的数据
ThreadLocalUtil.remove();
}
}
在这里补充一个
全局异常处理器
我们在写bug程序的时候,难免会遇到很多bug,通常我们会直接try - catch包裹住方法,然后处理掉。但是这样会让程序显得很臃肿,不方便别人观看。因此我们还是选择直接throw抛出异常让调用者处理。就这样一直抛一直抛,扔到最后,让一个专门的类来做统一处理,这就是全局异常处理。
代码实现
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
//有的异常可能没有message,所以要先用三元运算符判断一下
return Result.error(StringUtils
.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
}
}
简单解释一下,
- @RestControllerAdvice代表这是一个全局异常处理器。
- @ExceptionHandler()代表你要捕获什么异常,Exception.class代表你要捕获所有的异常,因为Exception是所有异常的
爹父类
你可能会疑问,我们最后return出去的不是result类吗,前端是怎么接受到json文件呢?
这是因为@RestControllerAdvice = @ControllerAdvice + @ResponesBody
@ResponesBody会将方法的返回值转化为Json