Servlet规范中所引入的filter令人心动不已,因为它引入了一个功能强大的拦截模式。Filter是这样一种Java对象,它能在request到达servlet的服务方法之前拦截HttpServletRequest对象,而在服务方法转移控制后又能拦截HttpServletResponse对象。你可以使用filter来实现特定的任务,比如验证用户输入,以及压缩web内容。但你拟富有成效地使用过滤器的念头却被你不能改变HttpServletRequest对象的参数的现实扫了兴,因为java.util.Map所包装的HttpServletRequest对象的参数是不可改变的。这极大地缩减了filter的应用范围。至少在一半的时间里,你希望可以改变准备传送给filter的对象。如果在HttpServletRequest对象到达Struts的action servlet之前,我们可以通过一个filter将用户输入的多余空格去掉,难道不是更美妙吗?这样的话,你就不必等到在Struts的action表单验证方法中才进行这项工作了。
幸运的是,尽管你不能改变不变对象本身,但你却可以通过使用装饰模式来改变其状态。
现在,让我们来看看,如何编写自己的HttpServletRequest装饰类。
一个删除空白字符的Filter
本节将以上的理论投入实际使用,通过实现一个删除空白字符的filter,来演示如何使用javax.servlet.http.HttpServletRequestWrapper类来装饰HttpServletRequest对象。在本例中,这个filter将删除所传来的参数中多余的空白字符。
这在许多servlet/JSP应用中是很有用的,包括Struts及JavaServer Faces等应用。例如,Struts通过调用HttpServletRequest对象的getParameterValues()对象来处理action表单。通过覆盖装饰类中此方法,你可以改变当前HttpServletRequest对象的状态。
要创建HttpServletRequest的装饰类,你需要继承HttpServletRequestWrapper并且覆盖你希望改变的方法。列表5中,MyRequestWrapper类将删除getParameterValues()方法返回值的多余空白字符。
列表5:HttpServerletRequest装饰类
程序代码:
package trimmer.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class MyRequestWrapper extends HttpServletRequestWrapper {
public MyRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String[] getParameterValues(String parameter) {
String[] results = super.getParameterValues(parameter);
if (results == null)
return null;
int count = results.length;
String[] trimResults = new String[count];
for (int i = 0; i < count; i++) {
trimResults[i] = results[i].trim();
}
return trimResults;
}
}
列表6演示了如何载获Http请求并装饰HttpServletRequest对象。[i]列表6:删除空白符的filter
列表6演示了如何载获Http请求并装饰HttpServletRequest对象。
[i]列表6:删除空白符的filter
程序代码:
package trimmer.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class MyFilter implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter initialized");
this.filterConfig = filterConfig;
}
public void destroy() {
System.out.println("Filter destroyed");
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new MyRequestWrapper((HttpServletRequest) request),
response);
}
}
这个程序使用了列表6所示的filter来修整用户输入。要使用这个filter,你需要在web.xml文件中如下设置filter及filter-mapping的元素。
<filter>
<filter-name>TrimmerFilter</filter-name>
<filter-class>trimmer.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TrimmerFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
要测试这个filter,启动这个应用后,在表单中输入一些值,提交表单,看看这个filter是如何修整输入数值的。这是一个实用的装饰模式的应用。
小结
Servlet filter可以在调用一个servlet的服务方法后,拦载或加工HTTP请求。尽管这非常诱人,但其实际使用却有所限制,因为你不能改变HttpServletRequest对象。
这时候装饰模式派上了用场。本文演示了如何通过应用装饰模式来“修改”HttpServletRequest对象,从而使你的servlet filter更加有用。在上面filter例子中,filter改了request参数中的用户输入,而这一点,如果没有装饰request对象,你是无论如何也不可能做到的。
---------------------------------------------------------
package wrapper;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {
private String charset = "UTF-8";
public GetHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 获得被装饰对象的引用和采用的字符编码
* @param request
* @param charset
*/
public GetHttpServletRequestWrapper(HttpServletRequest request,
String charset) {
super(request);
this.charset = charset;
}
/**
* 实际上就是调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换
*/
public String getParameter(String name) {
String value = super.getParameter(name);
value = value == null ? null : convert(value);
return value;
}
public String convert(String target) {
System.out.println("编码转换之前:" + target);
try {
return new String(target.trim().getBytes("ISO-8859-1"), charset);
} catch (UnsupportedEncodingException e) {
return target;
}
}
}
------------
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//设置请求响应字符编码
request.setCharacterEncoding(charset);
response.setCharacterEncoding(charset);
//新增加的代码
HttpServletRequest req = (HttpServletRequest)request;
if(req.getMethod().equalsIgnoreCase("get"))
{
req = new GetHttpServletRequestWrapper(req,charset);
}
System.out.println("----请求被"+config.getFilterName()+"过滤");
//传递给目标servlet或jsp的实际上时包装器对象的引用,而不是原始的HttpServletRequest对象
chain.doFilter(req, response);
System.out.println("----响应被"+config.getFilterName()+"过滤");
幸运的是,尽管你不能改变不变对象本身,但你却可以通过使用装饰模式来改变其状态。
现在,让我们来看看,如何编写自己的HttpServletRequest装饰类。
一个删除空白字符的Filter
本节将以上的理论投入实际使用,通过实现一个删除空白字符的filter,来演示如何使用javax.servlet.http.HttpServletRequestWrapper类来装饰HttpServletRequest对象。在本例中,这个filter将删除所传来的参数中多余的空白字符。
这在许多servlet/JSP应用中是很有用的,包括Struts及JavaServer Faces等应用。例如,Struts通过调用HttpServletRequest对象的getParameterValues()对象来处理action表单。通过覆盖装饰类中此方法,你可以改变当前HttpServletRequest对象的状态。
要创建HttpServletRequest的装饰类,你需要继承HttpServletRequestWrapper并且覆盖你希望改变的方法。列表5中,MyRequestWrapper类将删除getParameterValues()方法返回值的多余空白字符。
列表5:HttpServerletRequest装饰类
程序代码:
package trimmer.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class MyRequestWrapper extends HttpServletRequestWrapper {
public MyRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String[] getParameterValues(String parameter) {
String[] results = super.getParameterValues(parameter);
if (results == null)
return null;
int count = results.length;
String[] trimResults = new String[count];
for (int i = 0; i < count; i++) {
trimResults[i] = results[i].trim();
}
return trimResults;
}
}
列表6演示了如何载获Http请求并装饰HttpServletRequest对象。[i]列表6:删除空白符的filter
列表6演示了如何载获Http请求并装饰HttpServletRequest对象。
[i]列表6:删除空白符的filter
程序代码:
package trimmer.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class MyFilter implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter initialized");
this.filterConfig = filterConfig;
}
public void destroy() {
System.out.println("Filter destroyed");
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new MyRequestWrapper((HttpServletRequest) request),
response);
}
}
这个程序使用了列表6所示的filter来修整用户输入。要使用这个filter,你需要在web.xml文件中如下设置filter及filter-mapping的元素。
<filter>
<filter-name>TrimmerFilter</filter-name>
<filter-class>trimmer.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TrimmerFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
要测试这个filter,启动这个应用后,在表单中输入一些值,提交表单,看看这个filter是如何修整输入数值的。这是一个实用的装饰模式的应用。
小结
Servlet filter可以在调用一个servlet的服务方法后,拦载或加工HTTP请求。尽管这非常诱人,但其实际使用却有所限制,因为你不能改变HttpServletRequest对象。
这时候装饰模式派上了用场。本文演示了如何通过应用装饰模式来“修改”HttpServletRequest对象,从而使你的servlet filter更加有用。在上面filter例子中,filter改了request参数中的用户输入,而这一点,如果没有装饰request对象,你是无论如何也不可能做到的。
---------------------------------------------------------
package wrapper;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {
private String charset = "UTF-8";
public GetHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 获得被装饰对象的引用和采用的字符编码
* @param request
* @param charset
*/
public GetHttpServletRequestWrapper(HttpServletRequest request,
String charset) {
super(request);
this.charset = charset;
}
/**
* 实际上就是调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换
*/
public String getParameter(String name) {
String value = super.getParameter(name);
value = value == null ? null : convert(value);
return value;
}
public String convert(String target) {
System.out.println("编码转换之前:" + target);
try {
return new String(target.trim().getBytes("ISO-8859-1"), charset);
} catch (UnsupportedEncodingException e) {
return target;
}
}
}
------------
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//设置请求响应字符编码
request.setCharacterEncoding(charset);
response.setCharacterEncoding(charset);
//新增加的代码
HttpServletRequest req = (HttpServletRequest)request;
if(req.getMethod().equalsIgnoreCase("get"))
{
req = new GetHttpServletRequestWrapper(req,charset);
}
System.out.println("----请求被"+config.getFilterName()+"过滤");
//传递给目标servlet或jsp的实际上时包装器对象的引用,而不是原始的HttpServletRequest对象
chain.doFilter(req, response);
System.out.println("----响应被"+config.getFilterName()+"过滤");
}
如:filterChain.doFilter(new XssHttpServletRequestWrapper( (HttpServletRequest) request), response);
XssHttpServletRequestWrapper 是一个自定义继承HttpServletRequestWrapper的类