文章目录
- HTTP请求拦截器链
- 需求定义
- 写一个Controller方法接口
- 写三个http请求拦截器
- 把拦截器加入到配置中,并且配置拦截规则
- 在postman里面发送请求,看下测试结果是否正确
- 第三个参数的作用
HTTP请求拦截器链
需求定义
我们写一个包含三个HTTP请求拦截器的拦截器链,写一个controller控制器方法,最后在postman里面调用controller控制器里面的接口方法,看看这个请求的经过路径。
首先说结论,如下图:
写一个Controller方法接口
如下图:
写三个http请求拦截器
我们定义三个http请求拦截器如下图:
然后每个http请求拦截器都实现preHandle、postHandle、afterCompletion这三个方法,如下图:
preHandle、postHandle、afterCompletion方法的执行时机?
当一个http请求发过来的时候,如果没有http请求拦截器这个请求是会直接发送到Controller控制器里面的,但是如果有http请求拦截器的话,外部发来的http请求会先进入到拦截器中拦截。可能我们程序中有多个http请求拦截器,比如有三个http请求拦截器,那么这三个http请求拦截器会组成一个拦截器链,外部发来的http请求先进入第一个拦截器的preHandle方法,如果这个拦截器放行了,也就是preHandle方法返回了true,那么该请求就会被第一个拦截器放行,然后该请求会进入到拦截器链中的第一个拦截器中,同样是进入到preHandle方法当中,如果preHandle方法返回true,则同样放行;接着http请求进入到第三个拦截器的preHandle方法里面;最后该请求才会进入到controller控制器中执行。注意如果preHandle方法返回false,则该请求就不能传递到controller控制器中了。
执行完controller方法之后,该http请求就算是执行完毕了,接着会从拦截器链中倒着走出去,先走拦截器3的postHandle方法,再走拦截器2的postHandle方法,最后再走拦截器1的postHandle方法;
走完了postHandle方法之后,再走拦截器3的afterCompletion方法,再走拦截器2的afterCompletion方法,最后走拦截器1的afterCompletion方法;
这样就执行完了一个http请求的全部过程,流程图如下图:
把拦截器加入到配置中,并且配置拦截规则
我们需要写一个拦截器配置类,把需要用到的拦截器放到拦截器链中,并且配置每个拦截器拦截的http请求的规则,就是拦截什么样的http请求,比如只拦截/user的请求,或者只拦截/student的请求,具体是什么样的请求规则,我们可以自定义。拦截器配置类如下图:
可以看到我们这里把拦截器1、拦截器2、拦截器3全部都放到了拦截器链中,然后每个拦截器都是拦截所有的http请求。
在postman里面发送请求,看下测试结果是否正确
首先看下我们控制器里面接收请求的方法,如下图:
然后在postman里面发送一个/test请求,如下图:
可以看到我们的controller控制器里面的方法确实成功执行了,接着去看下在执行controller控制器方法之前,三个拦截器里面的preHandle方法是否执行了,以及在执行controller控制器方法之后,三个拦截器里面的postHandle方法和afterCompletion方法是否执行了,控制台输出信息如下图:
可以发现这里在执行Controller控制器之前确实执行了拦截器中的preHandle方法,以及在执行Controller控制器之后也确实执行了拦截器中的postHandle和afterCompletion方法,并且顺序也是正确的。
第三个参数的作用
如下图:
第三个参数的作用主要是:提供对当前请求所匹配的处理器的描述。
在SpringMVC中,handler参数通常是一个HandlerMethod对象实例,它包含了请求对应的Controller控制器以及调用的控制器里面的方法的信息。通过第三个参数handler,我们可以了解到具体是哪个Controller控制器,以及具体是控制器里面的哪个方法处理请求的。
比如有下面一个场景,当访问UserController控制器中的getUsers方法的时候,我们需要认证一下当前用户是不是“董事长”,只有是董事长,我们的认证拦截器才会通过,那我们该怎么利用第三个参数呢?代码如下:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class AuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取当前请求的处理器对象Object handlerObj = handler;// 判断处理器对象是否为Controller方法if (handlerObj instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handlerObj;// 获取控制器类和方法信息Class<?> controllerClass = handlerMethod.getBeanType();String methodName = handlerMethod.getMethod().getName();// 如果处理http请求的控制器是UserController,并且里面的处理方法是getUsers,这个时候我们需要在拦截器里面校验一下认证逻辑,看看当前用户是不是董事长,不是的话不放行if (controllerClass == UserController.class && "getUsers".equals(methodName)) {boolean result = authenticateUser(request);if (!result) {// 如果用户未通过身份验证,阻止请求继续执行return false;}}}// 如果身份验证通过,则允许请求继续执行return true;}private boolean authenticateUser(HttpServletRequest request) {// 通过request传递的参数得到当前用户姓名if (currentUserName.equals("董事长")) {return true;}return false;}
}
注意我们第三个参数handler也不全是Controller处理器对象,也可能是其他对象,但是如果是Controller处理器对象的话,它的类型就是HandlerMethod类型,因此我们这里才会有一个类型判断,如下图: