过滤器通过doFilter方法的第二个参数ServletResponse将输出发送给客户,但servletResponse参数没有为过滤器提供servlet或jsp页面的访问;执行doFilter方法时,servlet或jsp还没修改,当调用FilterChain的doFilter方法时,修改相应似乎为时已晚,以为数据已经发送给客户端,怎么办呢?
解决这个问题的办法是创建一个像HttpServletResponse的缓冲区,当servlet或jsp调用response.getWiter或者response.getOutputStream并发送输出时,输出实际上没有被发送到客户端,而是放到了该缓冲区中,这样filter就可以在发送到客户端前检查货修改这个输出。大概过程如下
具体代码如下:
1、实现HttpServletResponseWrapper
public class StringWrapper extends HttpServletResponseWrapper { private StringWriter stringWriter;//缓存区 public StringWrapper(HttpServletResponse response) { super(response); stringWriter = new StringWriter(); } /** 当servlet或jsp请求Writer时,给它们一个被封装过的writer, * 其会往缓冲区中写数据 */ public PrintWriter getWriter() { return(new PrintWriter(stringWriter)); } /** 同样,当 调用 getOutputStream, * 返回一个模拟的output stream */ public ServletOutputStream getOutputStream() { return(new StringOutputStream(stringWriter)); } /** 返回buffer的字符串表示 */ public String toString() { return(stringWriter.toString()); }
2、实现ServletOutputStreanm
public class StringOutputStream extends ServletOutputStream { private StringWriter stringWriter;//缓冲区引用 public StringOutputStream(StringWriter stringWriter) { this.stringWriter = stringWriter; } /**重写OutputStream write方法 * 这个方法被ServletOutputStream调用而ServletOutputStream extends OutputStream */ public void write(int c) { stringWriter.write(c); } }
看一下ServletOutputStream类print方法
public void print(String s) throws IOException { if (s == null) s = "null"; int len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); if ((c & 0xFF00) != 0) { String errMsg = lStrings.getString("err.not_iso8859_1"); Object[] errArgs = new Object[1]; errArgs[0] = new Character(c); errMsg = MessageFormat.format(errMsg, errArgs); throw new CharConversionException(errMsg); } write(c);//在这里被调用 } }
该类中其他所有输出String信息的方法都调用了该方法
疑问:当调用FilterChain 的doFilter方法时,怎么说就晚了呢?
response是一个引用,当调用filterChain doFilter(或者是servlet jsp,没有其他过滤器的话)后,响应就通过这个引用写到端口上去了,所以说为时已晚