拦截器(Interceptor)是用于在Spring MVC中对请求进行预处理和后处理的组件。可以把它想象成一个过滤器,它可以在请求到达控制器之前和响应返回给客户端之后执行一些操作。但是是在Filter过滤器之后执行。
(知道localhost:8080/127.0.0.1:8080 就能找到服务器地址和对应端口号)
具体执行流程为:过滤器Filter-DispatcherServlet-interceptor-controller
关于DispatcherServlet的理解,其实相当于一张路由表,打个比方,浏览器知道目的地("/login")但是不知道具体方位,DispatcherServlet为前端浏览器带路,带路到后端的("/login")的具体地点。折返也是同理。
拦截器(Interceptor),简单的说,他就跟你楼下保安的作用一样,经过保安检查才能让你进小区。
以下是具体的示例代码:
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//初入小区, 被保安检查。没问题让进,有问题不让进。
// 在请求到达controller之前执行
System.out.println("前置处理:请求路径 = " + request.getRequestURI());
return true; // 返回 true 继续处理,返回 false 中断请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//出门时, 被保安检查。但保安没权力阻止你出去。
// 在controller处理完请求后,但在视图渲染之前执行
System.out.println("后置处理:请求处理完毕");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//出门后, 保安关门。
// 在请求完成后执行(响应已经返回)
System.out.println("请求完成");
}
}
在controller处理完请求后,但在视图渲染之前执行?
关于视图的概念,普遍的来说是指的MVC(Model-View-Controller,模型-视图-控制器)架构中的
View.如下:
模型(Model):模型代表应用程序的数据和业务逻辑。它负责获取、存储和处理数据,比如从数据库中获取用户信息。
控制器(Controller):控制器是连接模型和视图的桥梁。它接收用户的输入(如点击按钮、提交表单等),调用模型进行数据处理,然后选择相应的视图来展示数据。
视图(View):视图是用户界面的表现部分。它从控制器接收数据,并将这些数据呈现给用户。在Web应用中,视图通常以HTML、CSS和JavaScript的形式存在。
虽然还是比较好理解了,但是还是比较难理解。
他们是怎样一种执行过程的呢?
用个比方可能会更好理解,有一家三口.
爸爸(model),负责搬砖(数据),任劳任怨,俗称老实人。
妈妈(controller),善于沟通,但不工作不搬砖,只会命令爸爸model搬砖(数据)。
女儿(View)是一位爱美的女孩,他需要钱买衣服打扮自己,但不会向操劳的父亲直接要钱,她只会向她妈要钱(数据),其实他妈只能找她爸要钱;当她要钱之后他会将钱转化为自己的打扮,将衣服(数据)显示在自己外表上。
View视图通常是动态的页面(展示后端数据)和静态的页面(不展示后端数据)的结合
那么视图的更具体的含义就引申出来了,女儿View 负责展示衣服(数据),这衣服就属于一个动态的页面,而女儿的身体就是一个衣服架子即是不会变化的静态页面。
女儿的衣服可以更换,但是身体确实始终不变的。动态页面中的数据可以变化,但是静态页面的数据不会变化。
在controller处理完请求后,但在视图渲染之前执行?
即是在女儿要到钱,换上新衣服之前。(代码中也提到的出门时保安检查)
即前端页面没有接收到后端返回结果之前。
就很好理解了。
然后就是拦截器的使用,除了示例代码之外还需要再实现WebMvcConfigurer接口,并重写addInterceptors方法
@Configuration
public class MyInterceptor implements WebMvcConfigurer {
//自定义的拦截器对象
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器对象
registry.addInterceptor(myCustomInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/login"); // 排除特定的请求
}
}
设置拦截器的请求路径
/* | 一级路径 | 能匹配/depts,/emps,/login,不能匹配 /depts/1 |
/** | 任意级路径 | 能匹配/depts,/depts/1,/depts/1/2 |
那么更具体一点讲解拦截器的基本使用:
拦截器在业务中通常用于处理跨切关注点,比如参数处理、安全检查等,以下就举参数处理和安全检查的例子:
参数处理:
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//参数处理
public class RequestParamInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String param = request.getParameter("param");
if (param != null) {
// 进行参数处理,比如去除空格、转换大小写等
param = param.trim().toLowerCase();
request.setAttribute("param", param);
}
return true;
}
}
安全检查(验证token):
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SecurityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//第一步,首先判断请求头中有没有携带token,有就继续没有返回
//第二部,验证token
return true;
}
}
更详细的代码:
//自定义拦截器
@Component //当前拦截器对象由Spring创建和管理
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
//前置方式
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle .... ");
//1.获取请求url
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
//3.获取请求头中的令牌(token)
String token = request.getHeader("token");
log.info("从请求头中获取的令牌:{}",token);
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(token)){
log.info("Token不存在");
//创建响应结果对象
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
//设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return false;//不放行
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("令牌解析失败!");
//创建响应结果对象
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
//设置响应头
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return false;
}
//6.放行
return true;
}