Java 捕获并记录响应正文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3242236/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:41:59  来源:igfitidea点击:

Capture and log the response body

javaservlets

提问by Jony

I have a servlet that handle certain HTTP requests and responses. I want to log the response body before sending back to the client. Is there any way that I can capture the response body before it is send as a HttpServletResponseobject from the servlet?

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

采纳答案by BalusC

If I understand you correctly, you want to log the response body? That's a pretty expensive task, but if that's the business requirement...

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

As @duffymo pointed, a Filteris a suitable place for this. You can capture the response body by replacing the passed-in ServletResponsewith a HttpServletResponseWrapperimplementation which replaces the HttpServletResponse#getWriter()with an own implementation which copies the response body into some buffer. After continuing the filter chain with the replaced response, just log the copy.

正如@duffymo 所指出的, aFilter是一个合适的地方。您可以通过将传入ServletResponseHttpServletResponseWrapper实现替换为一个实现来捕获响应正文,该实现将 替换为HttpServletResponse#getWriter()自己的实现,该实现将响应正文复制到某个缓冲区中。在使用替换的响应继续过滤器链后,只需记录副本。

Here's a kickoff example how the doFilter()method can look like:

以下是该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());
}

Here's how the CopyPrintWritercan look like:

这是罐子的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();
    }

}

Map this filter on an url-patternfor which you'd like to log responses for. Keep in mind that binary/static content like images, CSS, JS files and so on won't be logged this way. You'd like to exclude them by using a specific enough url-pattern, e.g. *.jspor just on the servlet-nameof the servlet in question. If you want to log binary/static content anyway (for which I don't see any benefit), then you need to replace the HttpServletResponse#getOutputStream()the same way as well.

将此过滤器映射到url-pattern您要为其记录响应的对象上。请记住,不会以这种方式记录二进制/静态内容,如图像、CSS、JS 文件等。您想通过使用足够具体的url-pattern,例如*.jsp或仅在servlet-name相关 servlet 的上排除它们。如果您无论如何都想记录二进制/静态内容(我没有看到任何好处),那么您也需要以HttpServletResponse#getOutputStream()相同的方式替换。

回答by duffymo

Maybe a servlet filtercan help you. Think of it as aspect-oriented programming for HTTP.

也许servlet 过滤器可以帮助您。可以将其视为 HTTP 的面向方面编程。

回答by pdorgambide

An alternative to BalusC answerUsing the TeeOutputStream to write into two outputstreams at time.

BalusC 答案的替代方法 使用 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();
}