文章摘录自微信公众号:java3y。Java3y同学的文章非常棒,内容详实。这篇文章是借用他的文章再做些修改。
1、什么是过滤器
首先,我们来看看过滤器究竟Web容器的哪处:
从上面的图我们可以发现,当浏览器发送请求给服务器的时候,先执行过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。。
我们很容易发现,过滤器可以比喻成一张滤网。我们想想现实中的滤网可以做什么:在泡茶的时候,过滤掉茶叶。那滤网是怎么过滤茶叶的呢?规定大小的网孔,只要网孔比茶叶小,就可以实现过滤了!
引申在Web容器中,过滤器可以做:过滤一些敏感的字符串【规定不能出现敏感字符串】、避免中文乱码【规定Web资源都使用UTF-8编码】、权限验证【规定只有带Session或Cookie的浏览器,才能访问web资源】等等等,过滤器的作用非常大,只要发挥想象就可以有意想不到的效果。
也就是说:当需要限制用户访问某些资源时、在处理请求时提前处理某些资源、服务器响应的内容对其进行处理再返回、我们就是用过滤器来完成的。
很多事情,你不需要在每个controller里面去写,比如编码,比如验证登录,统一都放到filter里面去做。过滤器相当于是后端资源的防火墙和大管家。
2 实际例子
上一个例子来演示过滤器的作用。
2.1 实现过滤器方法
主要需要实现方法的是doFilter。他能实现来回的过滤,那么都加上看看效果。
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterDemo1 init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("before doFilter");
//发送过去的时候过滤
HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest;
httpServletRequest.setAttribute("t1_Attr","sss");
//返回的时候过滤
HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse;
httpServletResponse.setHeader("myhead","hahaha");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("after doFilter");
}
@Override
public void destroy() {
System.out.println("FilterDemo1 destroy");
}
}
2.2 把这个filter注册进springboot。
这里描述了要过滤哪些链接
@Configuration
public class MyFilterConfig {
/**
* 注册Filter组件
*/
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
//设置自定义filter
bean.setFilter(new FilterDemo1());
//过滤哪一些请求
bean.setUrlPatterns(Arrays.asList("/test1/*"));
// bean.setUrlPatterns(Arrays.asList("/test1/*","/test2/*"));
return bean;
}
}
2.3 效果验证
访问后端前过滤了一道,在请求头加了Attribute,所以这里就能拿到值。
@RequestMapping("/test1")
@RestController
public class Test1 {
@RequestMapping("/t1")
public String T1(HttpServletRequest httpServletRequest){
String headName=(String)httpServletRequest.getAttribute("t1_Attr");
System.out.println(headName);
return "test1-t1";
}
}
返回给浏览器的时候,在响应头里面加了一个,所以浏览器中也能看到。
至此最基本的效果实现了。
3 多层过滤器
先新增两个过滤器
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo2 开始过滤");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("FilterDemo2 过滤结束");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo3 开始过滤");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("FilterDemo3 过滤结束");
}
然后分别注册进去,下面还设置了这两个过滤器的顺序。
@Bean
public FilterRegistrationBean myFilterTest2Controller1() {
FilterRegistrationBean bean = new FilterRegistrationBean();
//设置自定义filter
bean.setFilter(new FilterDemo2());
//指定顺序
bean.setOrder(1);
//过滤哪一些请求
bean.setUrlPatterns(Arrays.asList("/test2/*"));
return bean;
}
@Bean
public FilterRegistrationBean myFilterTest2Controller2() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new FilterDemo3());
//指定顺序
bean.setOrder(2);
//过滤哪一些请求
bean.setUrlPatterns(Arrays.asList("/test2/*"));
return bean;
}
得到下面的打印
FilterDemo2 开始过滤
FilterDemo3 开始过滤
test2访问到了
FilterDemo3 过滤结束
FilterDemo2 过滤结束
4、过滤器的简单应用小结
- 可以在filter中根据条件决定是否调用chain.doFilter(request, response)方法,即是否让目标资源执行。在上面的测试过滤器顺序的例子中,我们把两个过滤器的dofilter方法注释掉,可以发现controller的返回,页面上也看不到了,而且只执行了filter2的打印,因为水流直接在这里被截断了,都没流到filter3去。
- 在让目标资源执行之前,可以对request\response作预处理,再让目标资源执行。特别适合在响应头上做动作,返回给前端。
- 在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能