Servlet规范之过滤器Filter

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet规范之过滤器Filter相关的知识,希望对你有一定的参考价值。

文章是对 JSR-000340 JavaTM Servlet 3.1 Final Release的Java™ Servlet规范的翻译,尚未校准

Filtering

文章目录


过滤器是一种Java组件,允许对进入资源的请求和来自资源的响应中的负载和头信息进行即时转换。 Java Servlet API的类和方法为过滤活动和静态内容提供了一个轻量级框架。它描述了如何在Web应用程序中配置过滤器,以及实现过滤器的约定和语义。Servlet过滤器的API文档是在线提供的。过滤器的配置语法由第14章 "Deployment Descriptor "中的部署描述符模式给出。读者在阅读本章时应将这些资料作为参考。

什么是过滤器

过滤器是一段可重复使用的代码,它可以改变HTTP请求、响应和头信息的内容。过滤器一般不象servlets那样创建响应或对请求作出响应,而是修改或调整对资源的请求,并修改或调整来自资源的响应。过滤器可以作用于动态或静态内容。在本章中,动态和静态内容被称为Web资源。在需要使用过滤器的开发者可用的功能类型中,有以下几种:

  • The accessing of a resource before a request to it is invoked.在对一个资源的请求被调用之前,对该资源的访问。
  • The processing of the request for a resource before it is invoked.在资源被调用之前对其请求的处理。
  • The modification of request headers and data by wrapping the request in customized versions of the request object.通过将请求包裹在自定义版本的请求对象中来修改请求头和数据。
  • The modification of response headers and response data by providing customized versions of the response object.通过提供自定义版本的响应对象来修改响应头和响应数据。
  • The interception of an invocation of a resource after its call. 在资源调用后拦截它的调用。
  • Actions on a servlet, on groups of servlets, or static content by zero, one, or more filters in a specifiable order.通过零个、一个或多个过滤器以可指定的顺序对一个Servlet、Servlet组或静态内容进行操作。

过滤器使用场景

  • 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 转换XML内容的XSL/T过滤器
  • MIME-type chain filters MIME类型链过滤器
  • Caching filters 缓存过滤器

主要概念

本节将描述这个过滤模型的主要概念。应用程序开发人员通过实现javax.servlet.Filter接口并提供一个不需要参数的公共构造函数来创建一个过滤器。该类与构成Web应用程序的静态内容和Servlet一起被打包在Web Archive 中。使用部署描述符中的<filter>元素来声明过滤器。过滤器或过滤器的集合可以通过在部署描述符中定义 <filter-mapping> 元素来配置调用。这是通过将过滤器通过servlet的逻辑名称映射到一个特定的servlet,或者通过将过滤器映射到一个URL模式,映射到一组servlet和静态内容资源。

过滤器的生命周期

在部署 Web 应用程序之后,在请求导致容器访问 Web 资源之前,容器必须找到必须应用于 Web 资源的过滤器列表,如下所述。容器必须确保它已经为列表中的每个过滤器实例化了一个适当类别的过滤器,并调用其 init(FilterConfig config) 方法。过滤器可能会抛出一个异常,表示它不能正常工作。如果该异常是 UnavailableException 类型,容器可以检查该异常的isPermanent属性,并可以选择在以后的某个时间重试该过滤器。

部署描述符中的每个<filter> 声明只有一个实例被实例化到容器的JVM中。容器提供过滤器部署描述符中声明的过滤器config,对Web应用程序的ServletContext的引用,以及一组初始化参数。

当容器接收到一个传入的请求时,它将采取列表中的第一个过滤器实例,并调用其doFilter方法,传入ServletRequestServletResponse,以及它将使用的FilterChain对象的引用。

过滤器的 doFilter方法通常会按照这个模式或以下模式的某个子集来实现。

  1. 该方法检查了请求的头信息。

  2. 该方法可以用ServletRequestHttpServletRequest的自定义实现来包装请求对象,以便修改请求头或数据。

  3. 该方法可以用ServletResponseHttpServletResponse的自定义实现来包装传递给其doFilter方法的响应对象,以修改响应头或数据。

  4. 过滤器可以调用过滤器链中的下一个entity 。下一个entity 可能是另一个过滤器,或者如果调用的过滤器是这个链的部署描述符中配置的最后一个过滤器,下一个entity 就是目标Web资源。下一个entity 的调用是通过调用 FilterChain 对象上的 doFilter 方法来实现的,并传入被调用的请求和响应,或者传入它可能已经创建的封装版本。

    容器提供的过滤器链的doFilter`方法的实现必须定位过滤器链中的下一个entity ,并调用其doFilter方法,传入适当的请求和响应对象。

    或者,过滤器链可以通过不调用下一个entity来阻止请求,让过滤器负责填写响应对象。

    service方法需要与适用于servlet的所有过滤器在同一线程中运行。

  5. 在调用链中的下一个过滤器后,该过滤器可以检查响应头。

  6. 或者,过滤器可能抛出了一个异常,表明处理过程中出现了错误。如果过滤器在其doFilter处理过程中抛出一个 UnavailableException,容器就不能尝试继续沿着过滤器链进行处理。如果该异常没有被标记为永久性的,它可以选择在稍后的时间重试整个链。

  7. 当链中的最后一个过滤器被调用后,下一个被访问的entity是链末端的目标Servlet或资源。

  8. 在容器可以从服务中移除过滤器实例之前,容器必须首先调用过滤器的 destroy 方法,以使过滤器能够释放任何资源并执行其它清理操作。

包装请求与响应

过滤概念的核心是包装请求或响应的概念,以便它可以覆盖行为来执行过滤任务。在这个模型中,开发者不仅能够覆盖请求和响应对象上的现有方法,而且能够提供适合特定过滤任务的新API给过滤器或链上的目标网络资源。例如,开发者可能希望用更高层次的输出对象来扩展响应对象,如允许DOM对象被写回给客户端的API。

为了支持这种风格的过滤器,容器必须支持以下要求。当过滤器调用容器的过滤链实现上的doFilter方法时,容器必须确保它传递给过滤链中的下一个实体的请求和响应对象,或者如果过滤器是过滤链中的最后一个实体,则传递给目标网络资源的请求和响应对象是调用过滤器传递到doFilter方法中的同一个对象。

当调用者包装请求或响应对象时,对包装对象身份的要求同样适用于从Servlet或过滤器对RequestDispatcher.forwardRequestDispatcher.include的调用。在这种情况下,被调用的servlet所看到的请求和响应对象必须是调用的servlet或过滤器所传入的相同的包装器对象。

过滤器的执行环境

一组初始化参数可以使用部署描述符中的<init-params>元素与过滤器相关联。这些参数的名称和值在运行时通过过滤器的FilterConfig对象上的getInitParametergetInitParameterNames方法提供给过滤器。此外,FilterConfig允许访问Web应用程序的 ServletContext,以加载资源、记录功能,并在ServletContext的属性列表中存储状态。过滤器和过滤器链末端的目标Servlet或资源必须在同一个调用线程中执行。

在Web应用中配置过滤器

过滤器是通过规范第8-69页的8.1.2节"@WebFilter "中定义的@WebFilter 注解或在部署描述符中使用<filter>元素定义的。在这个元素中,程序员声明了以下内容:

  • filter-name: used to map the filter to a servlet or URL用来将过滤器映射到一个Servlet或URL上。
  • filter-class: used by the container to identify the filter type容器用于识别过滤器的类型
  • init-params: initialization parameters for a filter滤波器的初始化参数

作为可选项,程序员可以指定图标、文本描述和用于工具操作的显示名称。容器必须为部署描述符中的每个过滤器声明准确地实例化一个定义过滤器的 Java 类。因此,如果开发者为同一个过滤器类做了两个过滤器声明,那么同一个过滤器类的两个实例将被容器实例化。

Here is an example of a filter declaration:

<filter>
	<filter-name>Image Filter</filter-name>
	<filter-class>com.acme.ImageServlet</filter-class>
</filter>

一旦在部署描述符中声明了过滤器,汇编器就会使用<filter-mapping>元素来定义Web应用程序中要应用过滤器的servlet和静态资源。过滤器可以使用<servlet-name>元素与servlet关联。

例如,下面的代码示例将图像过滤器映射到ImageServletservlet:

<filter-mapping>
	<filter-name>Image Filter</filter-name>
	<servlet-name>ImageServlet</servlet-name>
</filter-mapping>

过滤器可以使用<url-pattern> 样式的过滤器映射与一组servlet和静态内容关联:

<filter-mapping>
	<filter-name>Logging Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

在这里,日志过滤器被应用于Web应用程序中的所有Servlet和静态内容页面,因为每个请求URI都与/*URL模式匹配。

当使用 <filter-mapping> 样式处理<url-pattern> 元素时,容器必须使用第12章 "Mapping Requests to Servlets"中定义的路径映射规则来确定<url-pattern>是否与请求URI匹配。

容器在建立应用于特定请求URI的过滤器链时使用的顺序如下:

  1. First, the <url-pattern> matching filter mappings in the same order that these elements appear in the deployment descriptor. 首先,<url-pattern>匹配过滤器映射,其顺序与这些元素在部署描述符中出现的顺序相同。
  2. Next, the <servlet-name> matching filter mappings in the same order that these elements appear in the deployment descriptor.接下来是<servlet-name>匹配的过滤器映射,其顺序与这些元素在部署描述符中出现的顺序相同。

如果一个过滤器映射同时包含<servlet-name><url-pattern>,容器必须将过滤器映射扩展为多个过滤器映射(每个<servletname><url-pattern>),保留<servlet-name><urlpattern>元素的顺序。例如,下面的过滤器映射:

<filter-mapping>
	<filter-name>Multipe Mappings Filter</filter-name>
	<url-pattern>/foo/*</url-pattern>
	<servlet-name>Servlet1</servlet-name>
	<servlet-name>Servlet2</servlet-name>
	<url-pattern>/bar/*</url-pattern>
</filter-mapping>

is equivalent to:

<filter-mapping>
	<filter-name>Multipe Mappings Filter</filter-name>
	<url-pattern>/foo/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>Multipe Mappings Filter</filter-name>
	<servlet-name>Servlet1</servlet-name>
</filter-mapping>
<filter-mapping>
	<filter-name>Multipe Mappings Filter</filter-name>
	<servlet-name>Servlet2</servlet-name>
</filter-mapping>
<filter-mapping>
	<filter-name>Multipe Mappings Filter</filter-name>
	<url-pattern>/bar/*</url-pattern>
</filter-mapping>

关于过滤器链的顺序的要求意味着容器在接收到一个传入的请求时,将按以下方式处理该请求:

  • 根据第 122 页的 "Specification of Mappings "的规则识别目标 Web 资源。
  • 如果有由 servlet 名称匹配的过滤器,并且 Web 资源有一个 <servlet-name>,那么容器将按照部署描述符中声明的顺序建立匹配的过滤器链。这个链中的最后一个过滤器与最后一个<servlet-name>匹配的过滤器相对应,是调用目标Web资源的过滤器。
  • 如果有使用 <url-pattern> 匹配的过滤器,并且 <url-pattern> 根据第 12.2 节 "Specification of Mappings "的规则与请求的 URI 匹配,则容器将按照部署描述符中声明的相同顺序构建 <url-pattern> 匹配的过滤器链。这个链中的最后一个过滤器是这个请求URI的部署描述符中最后一个<url-pattern>匹配过滤器。该链中的最后一个过滤器是调用<servlet-name>匹配链中的第一个过滤器的过滤器,如果没有,则调用目标Web资源。

预计高性能的Web容器将缓存过滤器链,以便它们不需要在每个请求的基础上计算它们。

过滤器与请求转发器

自Java Servlet规范的2.4版本以来,新的功能是配置过滤器,以便在请求调度器下调用forward()include()

通过在部署描述符中使用新的<dispatcher>元素,开发者可以为过滤器映射指出他是否希望过滤器在什么时候被应用到请求:

  1. The request comes directly from the client.请求直接来自客户端。

    This is indicated by a <dispatcher> element with value REQUEST, or by the absence of any <dispatcher> elements. 这由一个值为REQUEST的<dispatcher>元素表示,或者由没有任何<dispatcher>元素表示。

  2. The request is being processed under a request dispatcher representing the Webcomponent matching the <url-pattern> or <servlet-name> using a forward() call.请求正在由代表与<url-pattern>servlet-name>匹配的Webcomponent的请求调度器使用forward()调用进行处理。

    This is indicated by a <dispatcher> element with value FORWARD.这由一个值为FORWARD的<dispatcher>元素来表示。

  3. The request is being processed under a request dispatcher representing the Web component matching the <url-pattern> or <servlet-name> using an include() call.该请求正在由代表与<url-pattern><servlet-name>相匹配的Web组件的请求调度器使用include()调用进行处理。

    This is indicated by a <dispatcher> element with value INCLUDE.这由一个值为INCLUDE的<dispatcher>元素表示。

  4. The request is being processed with the error page mechanism specified in ”Error Handling” on page 108 to an error resource matching the <url-pattern>.该请求正在用第108页 "错误处理 "中指定的错误页面机制来处理与<url-pattern>匹配的错误资源。

    This is indicated by a <dispatcher> element with the value ERROR. 这将由一个值为ERROR的<dispatcher>元素来表示。

  5. The request is being processed with the async context dispatch mechanism specified in ”Asynchronous processing” on page 10 to a web component using a dispatch call.请求正在用第10页 "Asynchronous processing "中指定的异步上下文调度机制进行处理,到一个使用调度调用的Web组件。

    This is indicated by a <dispatcher> element with the value ASYNC. 这由一个值为ASYNC的<dispatcher>元素来表示。

  6. 或上述1、2、3、4或5的任何组合。

For example:

<filter-mapping>
	<filter-name>Logging Filter</filter-name>
	<url-pattern>/products/*</url-pattern>
</filter-mapping>

将导致日志过滤器被以/products/...开头的客户端请求调用,但在请求调度器有以/products/...开头的路径时,不会在请求调度器调用下调用。LoggingFilter将在请求的初始调度和恢复的请求中被调用。

下面的代码:

<filter-mapping>
	<filter-name>Logging Filter</filter-name>
	<servlet-name>ProductServlet</servlet-name>
	<dispatcher>INCLUDE</dispatcher>
</filter-mapping>

这将导致日志过滤器不被客户端对 ProductServlet的请求所调用,也不在对 ProductServlet的请求调度器forward()调用之下,而是在请求调度器include()调用之下被调用,其中请求调度器的名称以ProductServlet开头。

下面的代码:

<filter-mapping>
	<filter-name>Logging Filter</filter-name>
	<url-pattern>/products/*</url-pattern>
	<dispatcher>FORWARD</dispatcher>
	<dispatcher>REQUEST</dispatcher>
</filter-mapping>

这将导致日志过滤器被始于/products/...的客户端请求调用,同时在路径始于/products/...的请求调度器forward()的情况下被调用。

最后,以下代码使用了特殊的servlet名称’*'。

<filter-mapping>
	<filter-name>All Dispatch Filter</filter-name>
	<servlet-name>*</servlet-name>
	<dispatcher>FORWARD</dispatcher>
</filter-mapping>

这段代码将导致在请求调度器forward()调用时调用所有通过名称或路径获得的请求调度器的全部调度过滤器。

以上是关于Servlet规范之过滤器Filter的主要内容,如果未能解决你的问题,请参考以下文章

Spring 中拦截器与过滤器的区别

过滤器和拦截器的区别

Java深入 - Filter过滤器

Java深入 - Filter过滤器

Servlet过滤器——创建过滤器

Servlet过滤器----Filter