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