第6章 过滤
filter(过滤器)是Java servlet 2.3版API的新增特性。filter允许请求资源和资源的响应的载荷和header信息可以快速转换。
本章描述了这些新的servlet API类和方法,它们提供了一个轻量级框架来过滤激活的静态内容。描述了在web应用中如何配置filter,以及实现的约定和语义。
Servlet filter的API文档参见本文的API定义章节。filter的配置语法由第13章的DTD(document type definition)指定。读者在阅读本章时应该参考这些资源。
6.1 filter是什么?
filter是一段可重用的代码,可以转换HTTP请求,响应的内容和header信息。filter通常不会象servlet那样创建一个response或者对请求作出响应,而只是修改对资源的请求或来自资源的响应。
filter可以对动态或者静态内容起作用。对本章而言,动态和静态内容统称为web资源。
在下列函数类型之中,filter编写人员可利用的有
• 在请求函数调用之前访问资源。
• 在函数调用之前对资源请求的处理。
• 通过包装自定义的request对象中的请求对请求header和数据的修改。
• 通过提供自定义的response对象对response header和response数据的修改。
• 在函数调用之后拦截资源的调用。
• 0个,1个或多个filter以指定顺序对一个servlet,一组servlet或静态内容的动作。
6.1.1 过滤组件示例
• 认证filter
• 日志与审核filter
• 图片转换filter
• 数据压缩filter
• 加密filter
• 令牌filter
• 触发资源访问事件的filter
• 转换XML内容的XSL/T filter
• MIME类型链filter
• 缓存filter
6.2 主要概念
本节主要描述过滤模型概念。
应用开发人员通过实现javax.servlet.Filter接口,并提供一个无参的公共构造函数来创建filter。该类和组成web应用的静态内容和servlet一起打包成WAR文件。部署描述符中使用filter元素声明filter。filter或者filter集合可以在部署描述符中定义filter-mapping元素来进行配置。通过servlet的逻辑名称将filter映射到一个特定servlet,或者通过映射一个URL patter的filter来映射到一组servlet和静态内容资源。
6.2.1 filter生命周期
在web应用发布之后,请求引起容器访问web资源之前,容器必须定位下面所说的web资源中应用到的filter列表。容器必须确保已经实例化了列表中的每个filter并调用它的init(FilterConfig config)方法。如不能正确执行,filter可以抛出异常。如果异常类型为UnavailableException,容器可以检查异常的isPermanent属性,并且可能选择在以后某个时间重试filter。
容器的每个虚拟机只为配置描述文件中的每个filter的声明实例化一个实例。容器提供filter config作为filter配置描述符的声明,web应用的ServletContext引用,以及一组初始化参数。
当容器接收到请求时,将取得列表中的第一个filter实例,并调用它的doFilter方法,传入ServletRequest,ServletResponse和将使用的FilterChain对象的引用。
filter doFilter方法的实现通常遵循以下范式或者其中的一部分
第1步:方法检查请求的header。
第2步:它可以使用ServletRequest或者HttpServletRequest的自定义实现来包装request对象,以便修改请求header或数据。
第3步:它可以使用ServletResponse或者HttpServletResponse的自定义实现来包装传入doFilter方法的response对象,以便修改响应header或数据。
第4步: filter可以调用filter链中的下一个实体。下一个实体可能是另一个filter,或者如果发起调用的filter是部署描述符中配置的链的最后一个filter,下一个实体则是目标web资源。下一个实体的调用受调用FilterChain对象的doFilter方法,传入的请求和响应,或者可能已创建好的包装版本的影响。
由容器提供的filter链的doFilter方法的实现,必须定位到filter链中的下一个实体,并调用它的doFilter方法,传递合适的request和response对象。
filter链可以通过不调用下一个实体,让filter负责填充response对象以阻塞请求。
第5步:在链中的下一个filter调用之后,filter可能检查响应header。
第6步:filter可能已经抛出了一个异常,表示处理中有错误。如果filter在doFilter处理中抛出UnavailableException异常,容器不可以试图继续沿着filter链处理。如果异常没有标记为永久,它可以选择稍后重试整个链。
当链中的最后一个filter调用后,下一个访问的实体为链末端的目标servlet或资源。
在filter实例可以从容器服务中删除之前,容器必须先调用filter的destroy方法,以使filter能够释放任何资源,并执行其他清除操作。
6.2.2 包装请求和响应
过滤的核心概念是包装request和response,以便能重载行为以执行过滤任务。在这种模式中,开发人员不仅能够重载request和response对象已有的方法,还可以为针对链上的某个filter或目标web资源特定的过滤任务提供新的API。比如,开发人员可能想使用更高级的输出对象(输出流和writer)来扩展response对象,比如允许DOM对象写回客户端的API。
要支持这种形式的filter,容器必须支持以下需求。当一个filter调用容器的filter链实现的doFilter方法时,容器必须确保它传递给filter链的下一个实体的request和response对象(如果它是链中的最后一个filter,传递给目标web资源)和调用filter传递给doFilter方法的对象相同。
当开发人员将包装好的request和response对象传递给requestDispatcher时也有同样需求;传递给servlet调用的request和response对象必须与此相同。
6.2.3 filter环境
使用部署描述符的init-params元素,可以将一组初始化参数和filter关联。这些参数的名称和值filter运行时可通过filter的FilterConfig对象的getInitParameter或者getInitParameterNames方法使用。并且,FilterConfig提供对web应用ServletContext的访问,比如加载资源,日志功能,以及ServletContext属性列表中状态的存储。
6.2.4 Web应用中filter的配置
部署描述符中使用filter元素定义filter。在这元素中,程序员声明
• filter-name:用来将filter映射到servlet或者URL
• filter-class:容器用来识别filter类型
• init-params:filter的初始化参数
指定工具实现的图标,文本描述和显示名称是可选的。容器必须为部署描述符中每一个filter声明实例化一个定义filter的Java类实例。因此,如果开发人员用相同的filter类进行两次filter声明,那么容器将对同样的filter类实例化两个。
这里是一个filter声明的示例:
<filter>
<filter-name>Image Filter</filter-name>
<filter-class>com.acme.ImageServlet</filter-class>
</filter>
一旦filter在部署描述符中声明,集成人员使用filter-mapping元素定义web应用中要应用filter的servlet和静态资源。filter可以使用servlet-name元素关联到一个servlet。比如,下列将Image Filter filter映射到ImageServlet servlet:
<filter-mapping>
<filter-name>Image Filter</filter-name>
<servlet-name>ImageServlet</servlet-name>
</filter-mapping>
使用url-pattern形式的filter映射,filter可以和一组servlet和静态内容关联:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这是一个应用到web应用所有servlet和静态内容页面的Logging Filter,因为每一个请求URI都匹配“/*” URL模式。
当使用url-pattern形式的filter-mapping元素时,容器必须确定url-pattern是否匹配使用第11章中定义的路径映射规则的请求URI。
在构建filter链中容器对于特定请求URI应用的顺序是
1. url-pattern匹配的filter映射遵从部署描述符中这些元素出现的顺序
2. servlet-name匹配的filter映射遵从部署描述符中这些元素出现的顺序
这需求意味着容器在接收传入的请求时:
• 根据11.2节的规则识别目标web资源。
• 如果存在以servlet名称匹配的filter,并且web资源含servlet-name,那么容器将按照配置描述符中声明的顺序构建filter链。链中最后一个filter是最后一个servlet-name匹配filter,将由它来调用目标web资源。
• 如果存在使用url-pattern匹配的filter,并且url-pattern根据11.2节的规则匹配请求URI,那么容器将按照配置描述符中声明的顺序构建url-pattern匹配的filter链。链中的最后一个filter,是调用url-pattern匹配链中第一个filter,如果没有的话调用目标资源的或者是并且该filter将调用第一个servlet-name匹配的filter,如没有则直接调用目的web资源的filter。
若想提高性能, 容器最好缓存filter链,以免每次请求都要重新计算。