捕获并记录响应正文

时间:2010-07-13 23:06:07

标签: java servlets

我有一个处理某些HTTP请求和响应的servlet。我想在发送回客户端之前记录响应正文。有没有什么方法可以在从servlet作为HttpServletResponse对象发送之前捕获响应主体?

3 个答案:

答案 0 :(得分:27)

如果我理解正确,您想记录回复正文?这是一项非常昂贵的任务,但如果这是业务要求......

正如@duffymo指出的那样,Filter是一个合适的地方。您可以通过替换捕获响应主体传入的ServletResponseHttpServletResponseWrapper实现其HttpServletResponse#getWriter()用自己的实现将响应正文复制到一定的缓冲的替换。使用替换的响应继续过滤器链后,只需记录副本。

以下是doFilter()方法的示例:

public void doFilter(ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException {
    final CopyPrintWriter writer = new CopyPrintWriter(response.getWriter());
    chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
        @Override public PrintWriter getWriter() {
            return writer;
        }
    });
    logger.log(writer.getCopy());
}

以下是CopyPrintWriter的外观:

public class CopyPrintWriter extends PrintWriter {

    private StringBuilder copy = new StringBuilder();

    public CopyPrintWriter(Writer writer) {
        super(writer);
    }

    @Override
    public void write(int c) {
        copy.append((char) c); // It is actually a char, not an int.
        super.write(c);
    }

    @Override
    public void write(char[] chars, int offset, int length) {
        copy.append(chars, offset, length);
        super.write(chars, offset, length);
    }

    @Override
    public void write(String string, int offset, int length) {
        copy.append(string, offset, length);
        super.write(string, offset, length);
    }

    public String getCopy() {
        return copy.toString();
    }

}

将此过滤器映射到您要为其记录响应的url-pattern。请记住,二进制/静态内容(如图像,CSS,JS文件等)不会以这种方式记录。您希望使用足够的url-pattern来排除它们,例如*.jsp或仅在有问题的servlet的servlet-name上。如果您无论如何要记录二进制/静态内容(我看不出有什么好处),那么你就需要更换HttpServletResponse#getOutputStream()以同样的方式为好。

答案 1 :(得分:10)

BalusC answer的替代方案 使用TeeOutputStream在时刻写入两个输出流。

public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final PrintStream ps = new PrintStream(baos);

    chain.doFilter(req,new HttpServletResponseWrapper(res) {
         @Override
         public ServletOutputStream getOutputStream() throws IOException {
            return new DelegatingServletOutputStream(new TeeOutputStream(super.getOutputStream(), ps)
            );
         }
         @Override
         public  PrintWriter getWriter() throws IOException {
            return new PrintWriter(new DelegatingServletOutputStream (new TeeOutputStream(super.getOutputStream(), ps))
            );
         }
      });

    //Get Response body calling baos.toString();
}

答案 2 :(得分:2)

也许servlet filter可以帮到你。可以将其视为面向方面的HTTP编程。