【问题标题】:Capture and log the response body [duplicate]捕获并记录响应正文 [重复]
【发布时间】:2011-03-15 14:36:51
【问题描述】:

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

【问题讨论】:

    标签: java servlets


    【解决方案1】:

    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】:

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

    正如@duffymo 指出的那样,Filter 是一个合适的地方。您可以通过将传入的ServletResponse 替换为HttpServletResponseWrapper 实现来捕获响应主体,该实现将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()

    【讨论】:

    • @BalusC 当您实例化CopyPrintWriter 时,您已经通过调用getWriter 消耗了响应输出流,因此包装对象不再一致,导致IllegalStateException。跨度>
    • 你是对的,我完全错了!我用错了词,代码编译了,但是由于前面解释的原因没有运行
    • 已解决,请查看gist.github.com/4400492 以获取工作示例。对我来说,问题在于扩展 PrintWriter 而不是 ServletOutputStream。
    • @Muhammad:如果指定了@page pageEncoding,JSP 将设置它。但是,这个过滤器在 JSP 有机会设置它之前就已经获取了编写器,现在设置编码为时已晚。
    • spring boot 1.5.6 出现运行时错误:java.lang.IllegalStateException: getWriter() 已为此响应调用
    【解决方案3】:

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

    【讨论】:

    • 有趣的是,所有其他答案都引用了我的,但它不是被接受的......
    • 这确实是一个有用的提示(赞成),但错过了有关复制PrintWriter的提示
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-13
    • 2017-12-02
    • 2019-03-14
    • 2020-09-13
    • 1970-01-01
    • 2021-07-21
    相关资源
    最近更新 更多