我尝试实现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。它是原始ServletOutputStreamHttpServletResponse response

为什么?由于包装器重载了ServletResponse方法并提供给dofilter(..),因此它应使用包装器返回的流。

最佳答案

容器总是使用原始ServletOutputStreamServletResponse来将任何内容实际发送到客户端。

假设在特定的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的内容写回到客户端。

10-05 22:52