Filtering
过滤器(Filter)是 Java 组件, 允许运行过程中改变进入资源的请求和资源返回的响应中的有效负载和 header
信息。
本章描述了 Java Servlet v3.0 API 类和方法,它提供了一种轻量级的框架用于过滤动态和静态内容。还描
述了如何在 Web 应用配置 Filter和它们实现的约定和语义。
网上提供servlet过滤器的API文档在。过滤器的配置语法由第14章“部署描述符”中的部署描述符模式给出。阅读本章时,读者应使用这些来作为参考。
1. 什么是过滤器
过滤器是一段可重用的代码,它可以转换HTTP请求、响应和头信息的内容。过滤器通常不会像servlet那样创建响应或响应请求,而是修改或调整资源的请求,并修改或调整资源的响应。
过滤器可以作用于动态或静态内容。为了本章的目的,动态和静态内容被称为Web资源。
对于需要使用过滤器的开发人员可用的功能类型如下:
- 资源的访问请求之前调用它。
- 一个资源请求的处理之前调用。
- 通过包装定制版本的请求对象对请求头和数据的修改
- 通过提供定制版本的响应对象对响应头和响应数据的修改。
- 资源的调用的后拦截。
- 以指定的顺序通过零,一个或多个过滤器操作servlet、servlet、组或静态内容。
1.1 过滤器组件例子
- Authentication filters
- Logging and auditing filters
- Image conversion filters
- Data compression filters
- Encryption filters
- Tokenizing filters
- Filters that trigger resource access events
- XSL/T filters that transform XML content
- MIME-type chain filters
- Caching filters
2.主要概念
本章描述了过滤器模型的主要概念。
应用程序开发人员通过实现javax.servlet.Filter接口创建一个过滤器,并提供一个不带参数的公共构造函数。该类及构建Web 应用的静态资源和 Servlet 打包在 Web 应用归档文件中。在部署描述符中使用元素声明过滤器。通过在部署描述符中定义
元素,可以为过滤器或过滤器集合配置调用。可以使用 servlet 的
逻辑名把过滤器映射到一个特定的 servlet,或者使用 URL 模式把过滤器映射到一组 Servlet 和静态内容资源。
2.1 过滤器生命周期
The doFilter method of a filter will typically be implemented following this or some subset of the following pattern:
在部署Web应用程序之后,在请求导致容器访问Web资源之前,容器必须定位必须应用于Web资源的过滤器列表,如下所述。容器必须确保它已经为列表中的每个过滤器实例化了一个适当类的过滤器,并调用它的init(FilterConfig config)方法。这个过滤器可能会抛出一个异常来表示它不能正常工作。如果异常类型为UnavailableException,则容器可以检查异常的isPermanent属性,并在稍后的时间选择重试过滤器。
在部署描述符中,每个
当容器接收到一个传入请求时,它将在列表中获取第一个过滤器实例,并调用它的doFilter方法,传入ServletRequest和ServletResponse,并引用它将使用的FilterChain对象。
过滤器的doFilter方法通常会按照以下模式的某个子集实现:
该方法检查请求的header。
该方法可以用定制的ServletRequest或HttpServletRequest实现来包装请求对象,以修改请求头或数据。
该方法可以将响应对象封装到其doFilter方法中,并定制实现ServletResponse或HttpServletResponse来修改响应头或数据。
过滤器可以调用过滤器链中的下一个实体。下一个实体可能是另一个过滤器,或者如果调用的过滤器是这个链的部署描述符中配置的最后一个过滤器,那么下一个实体就是目标Web资源。下一个实体的调用是通过调用FilterChain对象上的doFilter方法来实现的,并在请求和响应中传递它可能已经创建的包装版本。过滤器链的doFilter方法的实现,由容器提供,必须在过滤器链中定位下一个实体,并调用它的doFilter方法,传入适当的请求和响应对象。或者,过滤器链可以阻止请求,而不是调用调用下一个实体,让过滤器负责填充响应对象。service方法需要在与应用于servlet的所有过滤器相同的线程中运行。
在调用链中的下一个过滤器之后,过滤器可以检查响应头。
或者,过滤器可能会抛出一个异常来指示处理中的错误。如果过滤器在其doFilter处理过程中抛出一个无法使用的异常,容器就不能尝试继续沿着过滤器链进行处理。如果异常不是永久性的,它可以选择在以后重新尝试整个链。
当调用链中的最后一个过滤器时,将访问的下一个实体是链末端的目标servlet或资源。
在过滤器实例可以被容器从服务中删除之前,容器必须首先调用过滤器上的destroy方法,以使过滤器能够释放任何资源并执行其他清理操作。
2.2包装请求和响应
过滤概念的核心是包装请求或响应的概念,以便它可以覆盖行为来执行过滤任务。在这个模型中,开发人员不仅有能力覆盖请求和响应对象上的现有方法,而且还能提供适合于特定筛选任务的新API,从而使过滤器或目标web资源沿着链向下。例如,开发人员可能希望将响应对象扩展到输出流或写入器的更高级别的输出对象,例如允许将DOM对象写回客户机的API。
为了支持这种类型的过滤器,容器必须支持以下要求。当一个过滤器调用容器的过滤器链上的doFilter方法实现中,容器必须确保它通过过滤器链中的下一个实体,或到目标web资源如果过滤器链中的最后一个的请求和响应对象,是通过调用过滤器传递到doFilter方法的相同的对象。
包装器对象标识的相同要求适用于从servlet或过滤器到RequestDispatcher的调用。或RequestDispatcher向前发展。包括,当调用者包装请求或响应对象时。在这种情况下,被调用的servlet所看到的请求和响应对象必须是通过调用servlet或过滤器传入的相同的包装器对象。
2.3 过滤器环境
在部署描述符中使用
2.4 在一个Web应用中filter的配置
过滤器是通过@WebFilter注释定义的,如在规范的第8-72页中定义的“@WebFilter”或使用部署描述符中的
过滤器名称:用于过滤器映射到一个servlet或URL
过滤器类:容器使用来识别过滤器类型
初始化:一个过滤器的初始化参数
可选地,程序员可以指定图标、文本描述和工具操作的显示名称。容器必须实例化定义在部署描述符中每个过滤器声明的过滤器的Java类的一个实例。因此,如果开发人员为同一个过滤器类生成两个过滤器声明,那么容器将实例化同一个过滤器类的两个实例。
下面是一个过滤器声明的例子:
Image Filter
com.example.ImageServlet
在部署描述符中声明了过滤器之后,汇编器使用filter-mapping>元素来定义Web应用程序中的servlet和静态资源。过滤器可以使用
Image Filter
ImageServlet
Filters can be associated with groups of servlets and static content using the
Logging Filter
/*
这里,日志过滤器应用于Web应用程序中的所有servlet和静态内容页,因为每个请求URI都匹配“/*”URL模式。
当使用
在构建用于特定请求URI的筛选器链时,容器使用的顺序如下:
- 首先,
匹配的过滤器映射与这些元素在部署描述符中出现的顺序相同。 - 接下来,
匹配的过滤器映射与这些元素在部署描述符中出现的顺序相同。
如果一个筛选映射包含了
Multiple Mappings Filter
/foo/*
Servlet1
Servlet2
/bar/*
is equivalent to:
Multipe Mappings Filter
/foo/*
Multipe Mappings Filter
Servlet1
Multipe Mappings Filter
Servlet2
Multipe Mappings Filter
/bar/*
关于过滤器链顺序的要求意味着容器在接收到传入请求时按如下方式处理请求:
■根据第126页上的“映射规范”的规则标识目标Web资源。
■如果存在通过servlet名称匹配的过滤器并且Web资源具有
■如果存在使用
预计高性能Web容器将缓存过滤器链,以便它们不需要按照每个请求计算它们。
2.5 Filters and the RequestDispatcher
自Java Servlet规范2.4版以来的新增功能是能够配置被请求调度器(request dispatcher)的forward()和include()调用的过滤器。
通过在部署描述符中使用新的
1.请求直接来自客户端。这由具有值REQUEST的
2.请求正在被表示匹配
3.请求正在代表匹配
呼叫。这由一个值为INCLUDE的
4.该请求正在使用第112页上的“错误处理”中指定的错误页机制处理为与
5.该请求正在使用第10页上的“异步处理”中指定的异步上下文分派机制,通过调度呼叫处理到Web组件。这由具有值ASYNC的
6.或上述1,2,3,4或5的任何组合。
例如:
Logging Filter
/products/*
会导致日志过滤器被客户端启动的/products/..请求调用,但不在请求分派器调用之下调用,其中请求分派器具有路径开始/ products/...。LoggingFilter将在请求最初的调度和恢复的请求时被调用。 以下代码:
Logging Filter
ProductServlet
INCLUDE
将导致日志过滤器不会被客户端请求调用到ProductServlet,也不会在请求dispatcher forward()调用下调用ProductServlet,而是会在请求调度程序include()调用中调用,其中请求分派器的名称以ProductServlet开始。下面的代码:
Logging Filter
/products/*
FORWARD
REQUEST
将导致Logging Filter被/products/...开头的请求和以/products/...开头的请求分发器forward()调用下调用。
最后,以下代码使用特殊的servlet名“*”
All Dispatch Filter
*
FORWARD
在按名字或按路径获取的所有请求分派器 forward()调用时该代码将导致 All Dispatch Filter 被调用。