Servlet规范中的filter引入了一个功能强大的拦截模式。Filter能在request到达servlet的服务方法之前拦截HttpServletRequest对象,而在服务方法转移控制后又能拦截HttpServletResponse对象。
你可以使用filter来实现特定的任务,比如验证用户输入、请求参数以及压缩web内容等操作。还可以在response输出页面内容之前,进行页面内容的过滤等操作。
1. HttpServletRequestWrapper
因为java.util.Map所包装的HttpServletRequest对象的参数是不可改变的。这极大地缩减了filter的应用范围。如果在HttpServletRequest对象到达Struts的action servlet之前,我们可以通过一个filter将用户输入的多余空格去掉,难道不是更美妙吗?这样的话,你就不必等到在Struts的action表单验证方法中才进行这项工作了。
幸运的是,可以使用avax.servlet.http.HttpServletRequestWrapper类来装饰HttpServletRequest对象。覆写getParameterMap()、getParameterValues()、getParameter()等方法来实现对请求参数的处理。
这在许多servlet/JSP应用中是很有用的,包括Struts及JavaServer Faces等应用。例如,Struts通过调用HttpServletRequest对象的getParameterValues()对象来处理action表单。通过覆盖装饰类中此方法,你可以改变当前HttpServletRequest对象的状态。
要创建HttpServletRequest的装饰类,你需要继承HttpServletRequestWrapper并且覆盖你希望改变的方法。
public class MyRequestWrapper extends HttpServletRequestWrapper { /** * 规范化后请求参数map */ private Map<String, String[]> sanitized; /** * 原始请求参数map */ private Map<String, String[]> orig; @SuppressWarnings("unchecked") public MyRequestWrapper(HttpServletRequest req) { super(req); orig = req.getParameterMap(); sanitized = getParameterMap(); } @Override public String getParameter(String name) { String[] vals = getParameterMap().get(name); if (vals != null && vals.length > 0) return vals[0]; else return null; } @SuppressWarnings("unchecked") @Override public Map<String, String[]> getParameterMap() { if (sanitized==null) sanitized = sanitizeParamMap(orig); return sanitized; } @Override public String[] getParameterValues(String name) { return getParameterMap().get(name); } /** * 规范请求参数 * @param raw * @return */ private Map<String, String[]> sanitizeParamMap(Map<String, String[]> raw) { Map<String, String[]> res = new HashMap<String, String[]>(); if (raw==null) return res; for (String key : (Set<String>) raw.keySet()) { String[] rawVals = raw.get(key); String[] snzVals = new String[rawVals.length]; for (int i=0; i < rawVals.length; i++) { snzVals[i] = xssEncode(rawVals[i]); } res.put(key, snzVals); } return res; } /** * 将特殊字符替换为全角 * @param s * @return */ private String xssEncode(String s) { if (s == null || s.isEmpty()) { return s; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '>': sb.append('>');// 全角大于号 break; case '<': sb.append('<');// 全角小于号 break; case '\'': sb.append('‘');// 全角单引号 break; case '\"': sb.append('“');// 全角双引号 break; case '&': sb.append('&');// 全角& break; case '\\': sb.append('\');// 全角斜线 break; case '/': sb.append('/');// 全角斜线 break; case '#': sb.append('#');// 全角井号 break; case '(': sb.append('(');// 全角(号 break; case ')': sb.append(')');// 全角)号 break; default: sb.append(c); break; } } return sb.toString(); } }
2. HttpServletResponseWrapper
可以在response输出页面内容之前,进行页面内容的过滤等操作。
比如知名的页面装饰框架sitemesh,就是利用filter过滤器先截获返回给客户端的页面,然后分析html代码并最终装饰页面效果后返回给客户端。
要截获页面返回的内容,整体的思路是先把原始返回的页面内容写入到一个字符Writer,然后再组装成字符串并进行分析,最后再返回给客户端。
public class ResponseWrapper extends HttpServletResponseWrapper{ private PrintWriter cachedWriter; private CharArrayWriter bufferedWriter; public ResponseWrapper(HttpServletResponse response) { super(response); // 这个是我们保存返回结果的地方 bufferedWriter = new CharArrayWriter(); // 这个是包装PrintWriter的,让所有结果通过这个PrintWriter写入到bufferedWriter中 cachedWriter = new PrintWriter(bufferedWriter); } @Override public PrintWriter getWriter() { return cachedWriter; } /** * 获取原始的HTML页面内容。 * * @return */ public String getResult() { return bufferedWriter.toString(); } }
然后再写一个过滤器来截获内容并处理:
public class MyServletFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 使用我们自定义的响应包装器来包装原始的ServletResponse ResponseWrapper wrapper = new ResponseWrapper( (HttpServletResponse) response); // 这句话非常重要,注意看到第二个参数是我们的包装器而不是response chain.doFilter(request, wrapper); // 处理截获的结果并进行处理,比如替换所有的“名称”为“铁木箱子” String result = wrapper.getResult(); result = result.replace("名称", "铁木箱子"); // 重置响应输出的内容长度 response.setContentLength(-1); // 输出最终的结果 PrintWriter out = response.getWriter(); out.write(result); out.flush(); out.close(); } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } }
有可能在运行的过程中页面只输出一部分,尤其是在使用多个框架后(比如sitemesh)出现的可能性非常大,在探究了好久之后终于发现原来是响应的ContentLength惹的祸。因为在经过多个过滤器或是框架处理后,很有可能在其他框架中设置了响应的输出内容的长度,导致浏览器只根据得到的长度头来显示部分内容。知道了原因,处理起来就比较方便了,我们在处理结果输出前重置一下ContentLength即可
// 重置响应输出的内容长度 response.setContentLength(-1); // 输出最终的结果 PrintWriter out = response.getWriter(); out.write(result); out.flush(); out.close();
这样处理后就不会再出现只出现部分页面的问题了!
小结
Servlet filter可以在调用一个servlet的服务方法后,拦载或加工HTTP请求。尽管这非常诱人,但其实际使用却有所限制,因为你不能改变HttpServletRequest对象。
这时候装饰模式派上了用场。本文演示了如何通过应用装饰模式来“修改”HttpServletRequest对象,从而使你的servlet filter更加有用。在上面filter例子中,filter改了request参数中的用户输入,而这一点,如果没有装饰request对象,你是无论如何也不可能做到的。
相关推荐
### Java Filter 打印请求返回参数详解 #### 一、背景与目的 在Web开发过程中,经常需要在请求处理流程中增加一些通用的功能,比如日志记录、性能监控、安全控制等。Java Servlet规范提供了Filter机制来实现这些...
- 过滤器可以通过`HttpServletRequestWrapper`和`HttpServletResponseWrapper`来修改请求和响应的编码。 7. **服务器配置**: - 除了在过滤器中设置,还可以在Tomcat的`server.xml`中全局设置Connector的`...
`HttpServletRequest`和`HttpServletResponse`接口分别用于获取请求参数和向客户端发送响应。 2. **JavaServer Pages (JSP)**: JSP是一种视图技术,它允许开发者在HTML中嵌入Java代码,简化了动态网页的开发。JSP...
- **interface: FilterConfig**:提供了访问Filter初始化参数和Filter上下文的方法。 ### 4. Sessions Session管理是Web应用中跟踪用户会话的重要手段。 - **interface: HttpSession**:提供了创建、管理和访问...
HttpServletRequest和HttpServletResponse的包装类,如HttpServletRequestWrapper和HttpServletResponseWrapper,是为了提供额外的功能或者改变默认行为。它们实现了原接口,并允许你覆盖或添加方法,以适应特定的...
测试Filter时,你可以访问由Container管理的`HttpServletRequestWrapper`、`HttpServletResponseWrapper`、`FilterConfigWrapper`和`FilterChain`对象。对于JSP测试,`JspTestCase`允许直接使用JSP的隐含对象进行...
5. 装饰器模式:在J2EE中,装饰器模式常用于动态地给对象添加新的行为或责任,例如HttpServletRequestWrapper和HttpServletResponseWrapper。 6. 适配器模式:在与不同标准或接口进行集成时,适配器模式可以帮助...
例如,可以使用`HttpServletRequestWrapper`和`HttpServletResponseWrapper`来实现这一功能。要改变某个方法的行为,通常需要继承这些包装类并重写相应的方法。 #### 总结 过滤器在Struts框架中扮演着关键角色,...
这个过滤器应该实现`Filter`接口,并在`doFilter`方法中调用`HttpServletRequestWrapper`和`ServletResponseWrapper`,以便Spring Security可以检查请求的合法性。 ```java public class SpringSecurityCxfFilter ...
通过实现`Filter`接口,重写`doFilter()`方法,设置请求和响应的编码,如`HttpServletRequestWrapper`和`HttpServletResponseWrapper`。 8. 字符集理解: 对于不同的字符集,如GBK、UTF-8、GB2312等,理解它们之间...
#### 2.2 `HttpServletRequestWrapper` 和 `HttpServletResponseWrapper` Spring Security通过包装原始的HTTP请求和响应对象,可以在不改变原有代码的情况下,添加额外的安全功能。 ### 3. **认证流程** Spring ...
1. **Servlet API**:在 Java Web 开发中,`HttpServletRequestWrapper` 和 `HttpServletResponseWrapper` 就是典型的装饰器模式应用,它们允许开发者在原始请求和响应对象的基础上添加额外的功能,如日志记录、安全...