Capturar e registrar o corpo da resposta

Eu tenho um servlet que manipula determinadas solicitações e respostas HTTP. Eu quero registrar o corpo da resposta antes de enviar de volta para o cliente. Existe alguma maneira que eu possa capturar o corpo da resposta antes que ele seja enviado como um object HttpServletResponse do servlet?

Se eu entendi corretamente, você deseja registrar o corpo da resposta? Essa é uma tarefa bem cara, mas se essa é a exigência comercial …

Como @duffymo apontou, um Filter é um local adequado para isso. Você pode capturar o corpo da resposta substituindo o ServletResponse transmitido por uma implementação HttpServletResponseWrapper que substitui o HttpServletResponse#getWriter() por uma implementação própria que copia o corpo da resposta em algum buffer. Depois de continuar a cadeia de filtros com a resposta substituída, registre a cópia.

Aqui está um exemplo inicial de como o método doFilter() pode se parecer:

 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()); } 

Veja como o CopyPrintWriter pode se parecer:

 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(); } } 

Mapeie esse filtro em um url-pattern para o qual você gostaria de registrar respostas. Tenha em mente que conteúdo binário / estático como imagens, CSS, arquivos JS e assim por diante não serão registrados desta maneira. Você gostaria de excluí-los usando um url-pattern suficiente específico, por exemplo, *.jsp ou apenas no servlet-name do servlet em questão. Se você quiser registrar conteúdo binário / estático de qualquer maneira (para o qual eu não vejo nenhum benefício), então você precisa replace o HttpServletResponse#getOutputStream() da mesma maneira também.

Talvez um filtro de servlet possa ajudá-lo. Pense nisso como programação orientada a aspectos para HTTP.

Uma alternativa para BalusC answer Usando o TeeOutputStream para escrever em dois streams de saída no tempo.

 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(); }