Filters are Java components that allow on the fly transformations of payload and header information in both the request into a resource and the response from a resource
过滤器是Java组件
这张描述了Java Servlet 3.0 APi类和方法,其为动态和静态内容提供了轻量级的过滤器框架。描述了如果在web应用程序中配置过滤器,以及实现的惯例和语义。
Servlet过滤器的API文档线上提供。其配置语法在第十四章中讲述,
6.1 What is a filter?(过滤器是什么)
一个过滤器是一个可重用的代码,可以改变HTTP请求的内容,响应和消息头信息。过滤器一般地不能创建一个响应或者如同Servlet做的那样一个请求对应一个响应,而是修改请求的一个资源,和从一个资源中修改或者适应响应。
过滤器可以对动态或者静态内容起作用。这章节的意图,动态和静态内容都是web资源。
开发者需要用到的过滤器如下:
1 调用请求前访问一个资源
2 调用前处理请求资源
3 在请求对象的自定义版本中,通过封装请求修改请求消息头和数据。
4 通过提供自定义版本的响应对象,修改响应对象消息头和响应数据
5 调用后资源调用的拦截
6 以指定的顺序,通过0个,1个或者多个过滤器操作一个Servlet,一组Servlet或者静态内容。
6.1.1 Examples of Filtering Components(过滤器组件例子)
1 Authentication filters
2 Logging and auditing filters
3 Image conversion filters
4 Data compression filters
5 Encryption filters
6 Tokenizing filters
7 Filters that trigger resource access events
8 XSL/T filters that transform XML content
9 MIME-type chain filters
10 Caching filters
6.2 Main Concepts(主要内容)
这节讲述过滤器的主要内容。
应用开发者实现javax.servlet.Filter接口创建一个过滤器并提供一个默认的构造器。这个类打包在与静态内容和servlet一起的Web文档中,构建了web应用程序。声明一个过滤器使用部署描述符<filter>。通过在部署描述符中定义<filter-mapping>,可以配置一个或者过滤器的集合。通过Servlet的逻辑名字映射过滤器的独特的Servlet,或者通过映射一个过滤器的URL模式,映射到一组servlets和静态内容。
6.2.1 Filter Lifecycle(过滤器的生命周期)
在部署应用程序后,在请求访问容器的资源之前,容器必须定位应用到web资源的过滤器列表。容器必须确定列表中的每个过滤器都实例了一个合适的类。过滤器可能抛出一个异常指明它不能正常执行。如果异常是UnavailableException,容器可能检查异常的isPermanent属性,可能选择在稍后的一段时间重试这个过滤器。
在部署描述符中每个<filter>声明的实例在容器的每个JVM中实例化。容器提供过滤器配置,如同过滤器部署描述符中申明的一样,还提供web应用程序的ServletContext的引用,和一组初始化参数。
当容器接受请求时,获取列表中的第一个过滤器并调用doFilter方法,传递ServletResponse和ServletRequest对象,和FilterChain对象的引用。
一个filter的doFilter方法一般实现以下步骤或者其中步骤中的几个:
1 这个方法检查请求的消息头
2 为了 修改请求消息头或数据,这个方法可能使用ServletRequest或者HttpServletRequest的自定义实现封装请求对象。
3 这个方法可能使用传递的响应对象参数,使用自定义实现的ServletResponse或者HttpServletResponse来修改响应消息头或数据
4 过滤器可能调用过滤器链中的下一个实体。下一个实体可能是另一个过滤器,或者这个过滤器调用的是部署描述符中配置的最后一个过滤器,下一个实体就是目标web资源。通过调用FilterChain的doFilter方法来调用下一个实体,并传递调用的请求和响应对象,或者传递它可能创建的封装的版本。
doFilter方法的过滤器链的实现,由容器提供,必须查找过滤器链的下一个实体,并调用doFilter方法,传递合适的请求和响应对象。
或者,过滤器链阻塞请求,通过不调用下一个实体,留下过滤器填满响应对象。
5 在调用过滤器链中的下一个过滤器之后,过滤器可能检查响应消息头。
6 或者,过滤器链可能抛出一个异常来指明处理中的错误。如果在doFilter处理中,过滤器抛出了一个UnavailableException,容器不必继续处理过滤器链。如果异常没有标示为永久,它可能选在在稍后的一段时间重试整个链。
7 当调用了链中的最后一个过滤器,访问的下一个实体是目标Servlet或者资源、
8 一个过滤器能被容器从业务中移除之前,容器必须首先调用这个过滤器的Destroy方法,使过滤器能释放任何资源并执行其他的清除工作。
6.2.2 Wrapping Requests and Responses(封装请求和响应)
过滤器的主要目的是封装一个请求或者响应,这样能重写其行为来执行过滤任务。在这个模式中,开发人员不仅仅可以重写请求和响应对象中的存在方法,也可以为链中的一个特别的过滤任务的过滤器或者资源提供适用的新的API。例如,开发人员希望用更高级的输出扩展响应对象,比如输出流或者writer,比如API,允许DOM对象写回客户端。
为支持这类过滤器,容器必须提供以下支持。当一个过滤器调用doFilter方法,容器必须保证请求和响应对象必须传递给链中的下一个过滤器,或者是目标web资源(如果这个过滤器已经是链的末端了),通过调用过滤器传递的是同样的对象到doFilter方法。
当调用封装的请求或者响应对象时,封装对象同样要求也适用从一个Servlet或者过滤器中调用。在这种情况下,请求和响应对象能被调用的Servlet所见,传递给下一个过滤器的的参数必须是相同的封装对象。
6.2.3 Filter Environment(过滤器环境)
一组初始化参数能通过部署描述符的<init-params>元素关联。这些参数的名字和值可以通过过滤器的FilterConfig对象的getInitParameter和getInitParameterNames方法的在运行期由过滤器访问。另外,FilterConfig负责访问web应用程序的ServletContext,来加载资源,日志功能,在ServletContext的属性列表中存储状态。过滤器链结尾处的过滤器和目标servlet或者资源必须在相同的调用线程中执行。
6.2.4 Configuration of Filters in a Web Application(Web应用程序中的过滤器的配置)
<filter>
<filter-name>Image Filter</filter-name>
<filter-class>com.acme.ImageServlet</filter-class>
</filter>
只要在部署描述符中申明了一个过滤器,汇编器使用<filter-mapping>元素在web应用程序中定义相关过滤器的servlet和静态资源。过滤器使用<servlet-name>元素关联一个servlet,下面代码例子映射Image Filter过滤器为ImageServlet servlet。
<filter-mapping>
<filter-name>Image Filter</filter-name>
<servlet-name>ImageServlet</servlet-name>
</filter-mapping>
过滤器可以使用<url-pattern>样式关联一组过滤器和静态资源内容。
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这里的日志过滤器适用web应用程序中的所有servlet和静态资源内容页面,因为每个请求URI匹配‘/’URL模式。
<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>
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<url-pattern>/foo/*</url-pattern>
<url-pattern>/foo/*</url-pattern>
<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>
6.2.5 Filters and the RequestDispatcher(过滤器和请求分发)
在servlet规范2.4中,就能在请求分发forward()和include()调用下,配置要调用的过滤器。
在部署描述符中使用<dispatcher>元素,开发者可以指定一个过滤器映射,无论是否它要这个过滤器适用这个请求:
1 请求直接来自客户端
通过<dispatcher>元素和值REQUEST指定,或者不需要这个元素来指定。
2 代表web组件匹配<url-pattern>或者<servlet-name>元素使用forward()调用的请求分发正在处理
使用值为FORWARD的<dispatcher>元素指明
3 代表web组件匹配<url-pattern> 或者 <servlet-name>元素使用include()调用的请求分发正在处理
使用值为INCLUDE的<dispacther>元素指明
4 使用错误页面机制指向匹配<url-pattern>的错误资源的请求正在处理
使用值为ERROR的<dispatcher>元素指明
5 使用异步上下文分发机制使用一个dispatch调用的web组件的请求处理
<dispatcher>元素值为ASYNC
6 上述集中情况的联合。
例如:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
</filter-mapping>
通过以/products/..为开始的客户端请求调用Logging Fliter。但不在以/products/...为开始的请求分发调用下。这个Logging Filter将会在请求初始调度和恢复请求的时候调用。下面的代码:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<servlet-name>ProductServlet</servlet-name>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
将导致Logging Filter不被客户端请求调用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>
将导致Logging Filter 被以/products/..开始的客户端请求调用,并且在以请求分发以/products/...为开始的路径的的forward()请求下调用
最终,下面的代码使用了专用的serlvet 名字'*'
<filter-mapping>
<filter-name>All Dispatch Filter</filter-name>
<servlet-name>*</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
这段代码导致了对于所有通过名字或者路径获取的请求分发,在请求分发的forward()调用下,将调用所有的分发过滤器。