表示层请求处理机制接收许多不同类型的请求,这些请求需要不同类型的处理。有些请求只是转发到适当的处理程序组件,而其他请求必须在进一步处理之前进行修改,审核或解压缩。
拦截过滤器模式的最好例子之一是Spring Security 的 DelegatingFilterProxy,它将拦截HTTP请求并进行身份验证检查。Spring安全性构建在过滤器链上。
让我们看看 拦截过滤器模式 如何通过示例解决问题。为简单起见,这种模式分为若干部分,如问题,动因,解决方案,实施等。
目录
问题
动因
解决方案
说明
结构 - 类图,序列图
参与者和责任
履行
后果
适用性
问题
(问题部分描述了开发人员面临的设计问题)
需要对客户端Web请求和响应进行预处理和后处理。当请求进入Web应用程序时,它通常必须在主处理阶段之前通过多个入口测试。例如,
动因
(本节描述了列出影响问题和解决方案的原因和动机。动因列表显示了人们可能选择并使用模式的理由)
解决方案
(此处解决方案部分简要介绍了解决方案的方法,并详细介绍了解决方案)
使用拦截过滤器作为可插拔过滤器来预处理和后处理请求和响应。过滤器管理器将松散耦合的过滤器组合在一个链中,将控制权委托给适当的过滤器。通过这种方式,您可以以各种方式添加、删除和组合这些过滤器,而无需更改现有代码。
结构
类图
序列图
参与者
过滤器 (Filter)- 在请求处理程序执行请求之前或之后执行特定任务的过滤器。
过滤链(Filter Chain) - 过滤链带有多个过滤器,有助于在目标上按照定义的顺序执行它们。
目标 (Target )- 目标对象是请求处理程序
过滤器管理器(Filter Manager) - 过滤器管理器管理过滤器和过滤器链。
客户端(Client) - 客户端是向Target对象发送请求的对象。
执行
第1步: 创建 过滤器( Filter) 界面。
<b>public</b> <b>interface</b> Filter {
<b>public</b> <b>void</b> execute(String request);
}
第2步: 创建具体过滤器 - AuthenticationFilter,DebugFilter。
<b>public</b> <b>class</b> AuthenticationFilter implements Filter {
<b>public</b> <b>void</b> execute(String request){
System.out.println(<font>"Authenticating request: "</font><font> + request);
}
}
</font>
<b>public</b> <b>class</b> DebugFilter implements Filter {
<b>public</b> <b>void</b> execute(String request){
System.out.println(<font>"request log: "</font><font> + request);
}
}
</font>
第3步: 创建 目标 (Target )
<b>public</b> <b>class</b> Target {
<b>public</b> <b>void</b> execute(String request){
System.out.println(<font>"Executing request: "</font><font> + request);
}
}
</font>
第4步: 创建 过滤链( Filter Chain)
<b>import</b> java.util.ArrayList;
<b>import</b> java.util.List;
<b>public</b> <b>class</b> FilterChain {
<b>private</b> List<Filter> filters = <b>new</b> ArrayList<Filter>();
<b>private</b> Target target;
<b>public</b> <b>void</b> addFilter(Filter filter){
filters.add(filter);
}
<b>public</b> <b>void</b> execute(String request){
<b>for</b> (Filter filter : filters) {
filter.execute(request);
}
target.execute(request);
}
<b>public</b> <b>void</b> setTarget(Target target){
<b>this</b>.target = target;
}
}
第5步: 创建 过滤器管理器( FilterManager)
<b>public</b> <b>class</b> FilterManager {
FilterChain filterChain;
<b>public</b> FilterManager(Target target){
filterChain = <b>new</b> FilterChain();
filterChain.setTarget(target);
}
<b>public</b> <b>void</b> setFilter(Filter filter){
filterChain.addFilter(filter);
}
<b>public</b> <b>void</b> filterRequest(String request){
filterChain.execute(request);
}
}
第6步: 创建 客户端 (Client)
<b>public</b> <b>class</b> Client {
FilterManager filterManager;
<b>public</b> <b>void</b> setFilterManager(FilterManager filterManager){
<b>this</b>.filterManager = filterManager;
}
<b>public</b> <b>void</b> sendRequest(String request){
filterManager.filterRequest(request);
}
}
步骤7: 使用 客户端 演示拦截过滤器设计模式
<b>public</b> <b>class</b> InterceptingFilterDemo {
<b>public</b> <b>static</b> <b>void</b> main(String[] args) {
FilterManager filterManager = <b>new</b> FilterManager(<b>new</b> Target());
filterManager.setFilter(<b>new</b> AuthenticationFilter());
filterManager.setFilter(<b>new</b> DebugFilter());
Client client = <b>new</b> Client();
client.setFilterManager(filterManager);
client.sendRequest(<font>"HOME"</font><font>);
}
}
</font>
第8步: 验证输出。
Authenticating request: HOME request log: HOME Executing request: HOME
标准过滤策略
servlet 2.3规范包括一个标准机制,用于构建过滤器链,并从这些链中不显眼地添加和删除过滤器。
过滤器围绕接口构建,并通过修改Web应用程序的部署描述符以声明方式添加或删除。
javax.servlet.Filter(接口)
过滤器是对资源请求(servlet或静态内容)或资源响应(或两者)执行过滤任务的对象。过滤器在doFilter方法中执行过滤。
每个Filter都可以访问FilterConfig对象,从中可以获取其初始化参数,例如,可以使用ServletContext的引用,以加载过滤任务所需的资源。
过滤器在Web应用程序的部署描述符中配置。
本设计中已确定的示例如下:
javax.servlet.FilterChain(接口)
FilterChain是servlet容器向开发人员提供的对象,它提供对资源的筛选请求的调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则调用链末尾的资源。
示例
这个策略的示例是创建一个过滤器,它对任何编码类型的请求进行预处理,以便在核心请求处理代码中对每个请求进行类似的处理。为什么有必要这样做?包含文件上载的HTML表单使用与大多数表单不同的编码类型。
因此,通过simplegetParameter()调用无法获得伴随上载的表单数据 。所以,我们创建了两个预处理请求的过滤器,将所有编码类型转换为单一的一致格式。我们选择的格式是将所有表单数据作为请求属性提供。
示例1 - 基本过滤器 - 标准过滤策略
<b>public</b> <b>class</b> BaseEncodeFilter implements
javax.servlet.Filter {
<b>private</b> javax.servlet.FilterConfig myFilterConfig;
<b>public</b> BaseEncodeFilter() { }
<b>public</b> <b>void</b> doFilter(
javax.servlet.ServletRequest servletRequest,
javax.servlet.ServletResponse servletResponse,
javax.servlet.FilterChain filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
filterChain.doFilter(servletRequest,
servletResponse);
}
<b>public</b> javax.servlet.FilterConfig getFilterConfig()
{
<b>return</b> myFilterConfig;
}
<b>public</b> <b>void</b> setFilterConfig(
javax.servlet.FilterConfig filterConfig) {
myFilterConfig = filterConfig;
}
}
<b>public</b> <b>class</b> StandardEncodeFilter
<b>extends</b> BaseEncodeFilter {
<font><i>// Creates new StandardEncodeFilter</i></font><font>
<b>public</b> StandardEncodeFilter() { }
<b>public</b> <b>void</b> doFilter(javax.servlet.ServletRequest
servletRequest,javax.servlet.ServletResponse
servletResponse,javax.servlet.FilterChain
filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
String contentType =
servletRequest.getContentType();
<b>if</b> ((contentType == <b>null</b>) ||
contentType.equalsIgnoreCase(
</font><font>"application/x-www-form-urlencoded"</font><font>)) {
translateParamsToAttributes(servletRequest,
servletResponse);
}
filterChain.doFilter(servletRequest,
servletResponse);
}
<b>private</b> <b>void</b> translateParamsToAttributes(
ServletRequest request, ServletResponse response)
{
Enumeration paramNames =
request.getParameterNames();
<b>while</b> (paramNames.hasMoreElements()) {
String paramName = (String)
paramNames.nextElement();
String [] values;
values = request.getParameterValues(paramName);
System.err.println(</font><font>"paramName = "</font><font> + paramName);
<b>if</b> (values.length == 1)
request.setAttribute(paramName, values[0]);
<b>else</b>
request.setAttribute(paramName, values);
}
}
}
</font>
示例2- MultipartEncodeFilter - 标准过滤策略
<b>public</b> <b>class</b> MultipartEncodeFilter <b>extends</b>
BaseEncodeFilter {
<b>public</b> MultipartEncodeFilter() { }
<b>public</b> <b>void</b> doFilter(javax.servlet.ServletRequest
servletRequest, javax.servlet.ServletResponse
servletResponse,javax.servlet.FilterChain
filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
String contentType =
servletRequest.getContentType();
<font><i>// Only filter this request if it is multipart </i></font><font>
</font><font><i>// encoding </i></font><font>
<b>if</b> (contentType.startsWith(
</font><font>"multipart/form-data"</font><font>)){
<b>try</b> {
String uploadFolder =
getFilterConfig().getInitParameter(
</font><font>"UploadFolder"</font><font>);
<b>if</b> (uploadFolder == <b>null</b>) uploadFolder = </font><font>"."</font><font>;
</font><font><i>/** The MultipartRequest class is:
* Copyright (C) 2001 by Jason Hunter
* <jhunter@servlets.com>. All rights reserved.
**/</i></font><font>
MultipartRequest multi = <b>new</b>
MultipartRequest(servletRequest,
uploadFolder,
1 * 1024 * 1024 );
Enumeration params =
multi.getParameterNames();
<b>while</b> (params.hasMoreElements()) {
String name = (String)params.nextElement();
String value = multi.getParameter(name);
servletRequest.setAttribute(name, value);
}
Enumeration files = multi.getFileNames();
<b>while</b> (files.hasMoreElements()) {
String name = (String)files.nextElement();
String filename = multi.getFilesystemName(name);
String type = multi.getContentType(name);
File f = multi.getFile(name);
</font><font><i>// At this point, do something with the </i></font><font>
</font><font><i>// file, as necessary</i></font><font>
}
}
<b>catch</b> (IOException e)
{
LogManager.logMessage(
</font><font>"error reading or saving file"</font><font>+ e);
}
} </font><font><i>// end if</i></font><font>
filterChain.doFilter(servletRequest,
servletResponse);
} </font><font><i>// end method doFilter()</i></font><font>
}
</font>
部署描述符 - 标准过滤策略
<filter>
<filter-name>StandardEncodeFilter</filter-name>
<display-name>StandardEncodeFilter</display-name>
<description></description>
<filter-<b>class</b>> corepatterns.filters.encodefilter.
StandardEncodeFilter</filter-<b>class</b>>
</filter>
<filter>
<filter-name>MultipartEncodeFilter</filter-name>
<display-name>MultipartEncodeFilter</display-name>
<description></description>
<filter-<b>class</b>>corepatterns.filters.encodefilter.
MultipartEncodeFilter</filter-<b>class</b>>
<init-param>
<param-name>UploadFolder</param-name>
<param-value>/home/files</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>StandardEncodeFilter</filter-name>
<url-pattern>/EncodeTestServlet</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>MultipartEncodeFilter</filter-name>
<url-pattern>/EncodeTestServlet</url-pattern>
</filter-mapping>
后果
适用性
使用截取过滤器模式时