06_Filtering

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 应用归档文件中。在部署描述符中使用元素声明过滤器。通过在部署描述符中定义<filter-mapping>元素,可以为过滤器或过滤器集合配置调用。可以使用 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属性,并在稍后的时间选择重试过滤器。

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

当容器接收到一个传入请求时,它将在列表中获取第一个过滤器实例,并调用它的doFilter方法,传入ServletRequest和ServletResponse,并引用它将使用的FilterChain对象。

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

  1. 该方法检查请求的header。

  2. 该方法可以用定制的ServletRequest或HttpServletRequest实现来包装请求对象,以修改请求头或数据。

  3. 该方法可以将响应对象封装到其doFilter方法中,并定制实现ServletResponse或HttpServletResponse来修改响应头或数据。

  4. 过滤器可以调用过滤器链中的下一个实体。下一个实体可能是另一个过滤器,或者如果调用的过滤器是这个链的部署描述符中配置的最后一个过滤器,那么下一个实体就是目标Web资源。下一个实体的调用是通过调用FilterChain对象上的doFilter方法来实现的,并在请求和响应中传递它可能已经创建的包装版本。过滤器链的doFilter方法的实现,由容器提供,必须在过滤器链中定位下一个实体,并调用它的doFilter方法,传入适当的请求和响应对象。或者,过滤器链可以阻止请求,而不是调用调用下一个实体,让过滤器负责填充响应对象。service方法需要在与应用于servlet的所有过滤器相同的线程中运行。

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

  6. 或者,过滤器可能会抛出一个异常来指示处理中的错误。如果过滤器在其doFilter处理过程中抛出一个无法使用的异常,容器就不能尝试继续沿着过滤器链进行处理。如果异常不是永久性的,它可以选择在以后重新尝试整个链。

  7. 当调用链中的最后一个过滤器时,将访问的下一个实体是链末端的目标servlet或资源。

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

2.2包装请求和响应

过滤概念的核心是包装请求或响应的概念,以便它可以覆盖行为来执行过滤任务。在这个模型中,开发人员不仅有能力覆盖请求和响应对象上的现有方法,而且还能提供适合于特定筛选任务的新API,从而使过滤器或目标web资源沿着链向下。例如,开发人员可能希望将响应对象扩展到输出流或写入器的更高级别的输出对象,例如允许将DOM对象写回客户机的API。

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

包装器对象标识的相同要求适用于从servlet或过滤器到RequestDispatcher的调用。或RequestDispatcher向前发展。包括,当调用者包装请求或响应对象时。在这种情况下,被调用的servlet所看到的请求和响应对象必须是通过调用servlet或过滤器传入的相同的包装器对象。

2.3 过滤器环境

在部署描述符中使用<initparams>元素,可以将一组初始化参数关联到一个过滤器。这些参数的名称和值可以通过getInitParameter和getInitParameterNames方法在运行时通过过滤器的FilterConfig对象进行筛选。此外,FilterConfig提供了对Web应用程序的ServletContext的访问,用于加载资源、记录功能,以及在ServletContext的属性列表中存储状态。过滤器和过滤器链末端的目标servlet或资源必须在相同的调用线程中执行。

2.4 在一个Web应用中filter的配置

过滤器是通过@WebFilter注释定义的,如在规范的第8-72页中定义的“@WebFilter”或使用部署描述符中的 <filter>元素 。在这个元素中,程序员声明如下:

  • 过滤器名称:用于过滤器映射到一个servlet或URL

  • 过滤器类:容器使用来识别过滤器类型

  • 初始化:一个过滤器的初始化参数

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

下面是一个过滤器声明的例子:

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

在部署描述符中声明了过滤器之后,汇编器使用filter-mapping>元素来定义Web应用程序中的servlet和静态资源。过滤器可以使用<servlet-name>元素与servlet关联。例如,下面的代码示例将图像过滤器过滤器映射到ImageServlet servlet:

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

Filters can be associated with groups of servlets and static content using the <urlpattern> style of filter mapping:

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

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

当使用<url-pattern>样式处理<filter-mapping>元素时,容器必须确定是否匹配请求URI,使用第12章中定义的路径映射规则,“将请求映射到servlet”。

在构建用于特定请求URI的筛选器链时,容器使用的顺序如下:

  1. 首先,<url-pattern>匹配的过滤器映射与这些元素在部署描述符中出现的顺序相同。
  2. 接下来,<servlet-name>匹配的过滤器映射与这些元素在部署描述符中出现的顺序相同。

如果一个筛选映射包含了<servlet-name> 和<url-pattern>,那么容器必须将筛选映射扩展到多个筛选映射(每个<servletname> and <url-pattern>),保存<servlet-name> and <urlpattern>元素的顺序。例如,下面的筛选器映射:

<filter-mapping>
<filter-name>Multiple 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>

关于过滤器链顺序的要求意味着容器在接收到传入请求时按如下方式处理请求:
■根据第126页上的“映射规范”的规则标识目标Web资源。
■如果存在通过servlet名称匹配的过滤器并且Web资源具有<servletname>,那么容器会按照部署描述符中声明的顺序构建匹配的过滤器链。该链中的最后一个过滤器对应于最后一个<servlet-name>匹配过滤器,并且是调用目标Web资源的过滤器。
■如果存在使用<url-pattern>匹配的过滤器并且<url-pattern>根据第12.2节“映射规范”的规则匹配请求URI,则容器会构建匹配过滤器的<url-pattern>链与部署描述符中声明的顺序相同。此链中的最后一个过滤器是此请求URI的部署描述符中的最后一个<url-pattern>匹配过滤器。该链中的最后一个过滤器是调用<servlet-name>匹配链中的第一个过滤器的过滤器,或者调用目标Web资源(如果没有)。

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

2.5 Filters and the RequestDispatcher

自Java Servlet规范2.4版以来的新增功能是能够配置被请求调度器(request dispatcher)的forward()和include()调用的过滤器。
通过在部署描述符中使用新的<dispatcher>元素,开发人员可以指出过滤器映射是否希望在以下情况下将过滤器应用于请求:
1.请求直接来自客户端。这由具有值REQUEST的<dispatcher>元素或没有任何<dispatcher>元素指示。
2.请求正在被表示匹配<url-pattern>或<servlet-name>的Web组件的请求调度程序下使用forward()调用所处理。这由具有值FORWARD的<dispatcher>元素指示。
3.请求正在代表匹配<url-pattern>或<servlet-name>的Web组件的请求分派器下使用include()进行处理。
呼叫。这由一个值为INCLUDE的<dispatcher>元素表示。
4.该请求正在使用第112页上的“错误处理”中指定的错误页机制处理为与<url-pattern>匹配的错误资源。这由具有值ERROR的<dispatcher>元素指示。
5.该请求正在使用第10页上的“异步处理”中指定的异步上下文分派机制,通过调度呼叫处理到Web组件。这由具有值ASYNC的<dispatcher>元素指示。
6.或上述1,2,3,4或5的任何组合。
例如:

<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,也不会在请求dispatcher forward()调用下调用ProductServlet,而是会在请求调度程序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()调用下调用。
最后,以下代码使用特殊的servlet名“*”

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

在按名字或按路径获取的所有请求分派器 forward()调用时该代码将导致 All Dispatch Filter 被调用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,517评论 18 139
  • Filters是Java组件,它们在从请求到资源及从资源到响应上允许有效负荷与头部信息的传递。 本章描述了Java...
    Lucky_Micky阅读 833评论 0 0
  • 仅作为自己学习记录使用,文章来自: 1、http://blog.csdn.net/csh624366188/art...
    BakerZhang阅读 1,005评论 1 5
  • 这部分主要是与Java Web和Web Service相关的面试题。 96、阐述Servlet和CGI的区别? 答...
    杂货铺老板阅读 1,391评论 0 10
  • 我是日记星球26号朵女郎星宝宝,正在参加孙老师的第3期21天蜕变之旅,这是我的第202天原创日记,我相信日积月积的...
    晴致生活馆阅读 250评论 0 0