11-过滤器

## 介绍

首先来看,过滤器在整个Web流程的哪里:

image-20200815085855245

从上面的图,我们可以看出来,当浏览器发送请求给服务器时,会先执行过滤器,再访问Web资源;服务器响应Response时,也会先执行过滤器,再发送请求资源给浏览器。

作用

  • 拦截Http服务器,帮助Http服务器检测当前请求合法性
  • 拦截Http服务器,对当前请求进行增强操作

例如,在没有过滤器时,如果要处理中文乱码情况,需要在每个Sevlet中都设定编码,但是这样,代码重复率很高;有了过滤器,情况就不一样了,只要在过滤器中指定了编码,那么就可以使全站的Web资源都是使用该编码。

当然,过滤器的作用很多,还可以过滤一些敏感的字符串,权限验证(规定只有带Session或Cookie的浏览器才能访问web资源)等等。

Filter接口(过滤器接口)

  • 来自于Sevlet规范下接口
  • Filter接口实现类由开发人员提供,Http服务器不提供实现

Filter接口开发类步骤

  • 创建一个Java类实现Filter接口
  • 重写Filter接口中的doFilter方法
  • 在web.xml文件中将过滤器接口实现类注册到Http服务器

Filter接口 API

Filter接口有三个方法

  • void init(FilterConfig filterconfig)
  • void destroy()
  • void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)

前两个方法很好理解,它们只在Web服务器加载和销毁时被执行,且只会被执行一次。

比较重要的是doFilter方法:有三个参数,前面两个参数和doGet,doPost方法参数一样,请求参数和响应参数,ServletRequest是HttpServletRequest的父接口。

第三个参数FilterChain类型,也是一个接口,该接口只有一个方法:doFilter(ServletRequest var1, ServletResponse var2)。

感觉有点糊涂了,怎么有一个doFilter方法。

可以这样理解:过滤器不止一个,那么就需要管理这些过滤器,在Java中使用了链式结构,把所有的过滤器都放在了FilterChain中,如果符合条件,就执行下一个过滤器,一直到没有符合条件的过滤器,就执行目标资源。

简单来说,FilterChain掌管着所有过滤器,执行完一个,由FilterChain去判断是否还有符合条件的下一个过滤器,如果有,继续执行下一个过滤器,没有,就执行目标资源。

一个简单的例子

首先写一个简单的过滤器:设置当浏览器访问timg.jpg图片资源时,如果年龄小于等于40,就可以访问,大于40,就不能访问,并输出提示信息。

WebFilter代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class WebFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

//获得年龄参数
String age = servletRequest.getParameter("age");

if(Integer.valueOf(age) <= 40) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
//如果年龄大于40,就输出提示信息,没执行filterChain.doFilter方法,就会直接返回给浏览器
servletResponse.setContentType("text/html;charset=utf-8");
PrintWriter out = servletResponse.getWriter();
out.print("<h1>你不能查看此资源!<h1>");
}

}
}

然后需要在web.xml文件中进行注册:

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>webFilter</filter-name>
<filter-class>filter.WebFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>webFilter</filter-name>
<url-pattern>/timg.jpg</url-pattern>
</filter-mapping>

这个例子中,希望过滤器当浏览器访问timg.jpg文件时,进行拦截。

filter标签中有:

  • filter-name:用于为过滤器设置一个名字

  • filter-class:是过滤器实现类的完整限定类名

  • init-param:该元素用于为过滤器指定初始化参数,它的子元素param-name指定参数名字,param-value指定参数的值,在过滤器中可以使用FilterConfig接口对象来访问初始化参数

filter-mapping:是用来设置当浏览器访问哪些资源时,哪个过滤器需要进行拦截

可以有两种方式来指定:Sevlet名称,资源访问的请求路径

filter-mapping标签中有:

  • filter-name:写上需要使用的过滤器名字,该值必须是在filter中声明过的
  • url-pattern:设置filter所拦截的请求路径
  • servlet-name:指定过滤器所拦截的Servlet名称
  • dispatcher:指定当用户以什么样的方式访问资源时,过滤器需要拦截,有四个可取值:REQUEST,INCLUDE,FORWORD,ERROR, 默认为REQUEST,用户可以设置多个标签子元素来指定Filter对资源的多种调用方式进行拦截。
    • REQUEST:当用户直接访问页面时,Web容器将会调用此过滤器。如果目标资源是通过RequestDispatcher的include()或forword方法访问时,那么该过滤器就不会被调用
    • INCLUDE:如果目标资源是通过RequestDispatcher的include方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用
    • FORWORD:如果该目标资源是通过RequestDispatcher的forword方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
    • ERROR:如果目标资源使用过声明式异常处理机制被调用时,那么该过滤器将被调用,除此之外,过滤器不会被调用

过滤器的执行顺序

首先看一个测试:

1
2
3
4
5
6
7
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("准备放行");

filterChain.doFilter(servletRequest, servletResponse);

System.out.println("放行成功");
}

执行结果为:

image-20200815143152988

完整的执行流程为:客户端发送请求给Http服务器,服务器执行过滤器,先打印"准备放行",接着执行doFilter方法,服务器发现没有其他过滤器,就执行目标资源,执行完以后,再回到过滤器,继续执行代码,打印"放行成功"。

再看一个测试:

有两个过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//Filter1

public class Filter1 implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("开始执行Filter1");

filterChain.doFilter(servletRequest, servletResponse);

System.out.println("Filter1执行结束");
}
}


//Filter2

public class Filter2 implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

System.out.println("开始执行Filter2");

filterChain.doFilter(servletRequest, servletResponse);

System.out.println("Filter2执行结束");

}
}

测试的Servlet的doGet方法:

1
2
3
4
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

System.out.println("执行Servlet代码");
}

执行结果为:

image-20200815144016042

由该执行结果可知,执行顺序为:先执行Filter1,然后执行Filter1的doFilter方法,就转到下一个过滤器Filter2,等Filter2执行完后,再回到Filter1继续执行。

其中,过滤器之间的执行顺序是看在web.xml文件中mapping的先后顺序的,放在前面的先执行。

Filter的简单应用

filter有三种典型应用:

  • 可以在filter中根据条件来决定是否调用filterChain.doFilter()方法,即是否让目标资源继续执行
  • 在让目标资源执行之前,可以对request\response作预处理,再让目标资源执行
  • 在目标资源执行之后,可以捕获目标资源执行结果,从而实现一些特殊功能

过滤器拦截地址 url-pattern 格式

在web.xml文件中,以下语法用来定义映射:

  • 以 “/” 和 “/*” 结尾的是用来做路径映射
  • 以前缀 “*.” 开头的是用来做 拓展映射
  • “/” 是用来定义default servlet映射的
  • 剩下的都是用来定义详细映射的,比如:/aa/bb/cc.jpg

有几种情况:

  • 在调用某一具体文件时,拦截格式:/img/test.jpg
  • 在调用某一文件下的资源文件时,拦截格式:/img/*
  • 在调用任意文件夹下某种类型文件时,拦截格式:*.jpg
  • 在调用网站任意文件时,拦截格式:/*

注意:定义例如 “/*.action”是错误的,因为这既属于路径映射,又属于拓展映射,服务器无法分辨


11-过滤器
https://zhaoquaner.github.io/2022/05/11/JavaWeb/Servlet/12-过滤器/
更新于
2022年5月22日
许可协议