我尝试实现HttpServletResponseWrapper以便读取servlet响应的内容。我可以成功读取过滤器中的内容。但是从应用程序发出的最后一个数据包的主体为空(请使用浏览器和提琴手检查)
实作
class ReadableContentHttpServletResponse extends HttpServletResponseWrapper {
private ByteArrayOutputStream outputStream;
private ServletOutputStream servletOutputStream;
public ReadableContentHttpServletResponse(HttpServletResponse response) {
super(response);
outputStream = new ByteArrayOutputStream();
servletOutputStream = new ServletOutputStream() {
private WriteListener writeListener = null;
@Override
public void write(int b) throws IOException {
outputStream.write(b);
if (writeListener != null) {
writeListener.notify();
}
}
@Override
public void setWriteListener(WriteListener writeListener) {
this.writeListener = writeListener;
}
@Override
public boolean isReady() {
//ByteArrayOutputStream.close() has not effect
return true;
}
};
}
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(outputStream);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return servletOutputStream;
};
//... Getters...
}
过滤器
...
ReadableContentHttpServletResponse wrappedResponse = new ReadableContentHttpServletResponse(httpResponse);
chain.doFilter(request,wrappedResponse);
log.debug("content = " + wrappedResponse.getContent()); //working
log.debug("content = " + wrappedResponse.getContent()); //working
我尝试多次读取过滤器中的
ByteArrayOutputStream
(有效),但是最终主体的数据包仍然为空。我还尝试了Apache的ByteArrayOutputStream
。请注意,只有一个过滤器,并且我删除了
dofilter
之后的所有内容。编辑:用于生成最终数据包的流不是包装器的
ServletOutputStream
。它是原始ServletOutputStream
的HttpServletResponse response
。为什么?由于包装器重载了
ServletResponse
方法并提供给dofilter(..)
,因此它应使用包装器返回的流。 最佳答案
容器总是使用原始ServletOutputStream
的ServletResponse
来将任何内容实际发送到客户端。
假设在特定的Filter
中有三个FilterChain
,分别为F1,F2和F3,并且F2希望修改链末端的任何资源产生的数据。在这种情况下,将发生以下事件:
使用实例R作为doFilter()
参数调用F1的ServletResponse
,该实例又持有对ServletOutputStream
S的引用。F1调用FilterChain.doFilter(..., R)
。
F2的doFilter()
也可以用R和S调用。由于F2要修改响应,因此将R包装在例如HttpServletResponseWrapper
实现R'提供了替代ServletOutputStream
S'。 F2调用FilterChain.doFilter(..., R')
。
因此,F3的doFilter()
用参数R'和S'调用,并调用`FilterChain.doFilter(...,R')。
客户请求的目标资源将数据写入S'。
F3的doFilter()
返回。
F2刷新S',对其内容进行处理,然后将修改后的有效负载写入S。除非有意让F1-最终希望客户端-接收到空响应,否则它实际上不能这样做。 F2的`doFilter()返回。
F1的doFilter()返回,并且S的内容写回到客户端。