2010-07-13 14 views
19

Tengo un servlet que maneja ciertas solicitudes y respuestas HTTP. Quiero registrar el cuerpo de la respuesta antes de enviar de vuelta al cliente. ¿Hay alguna manera de que pueda capturar el cuerpo de la respuesta antes de que se envíe como un objeto HttpServletResponse del servlet?Capturar y registrar el cuerpo de la respuesta

Respuesta

25

Si lo entiendo correctamente, ¿desea registrar la respuesta cuerpo? Esa es una tarea bastante costosa, pero si ese es el requisito comercial ...

Como @duffymo apuntado, un Filter es un lugar adecuado para esto. Puede capturar el cuerpo de la respuesta reemplazando el ServletResponse pasado con una implementación HttpServletResponseWrapper que reemplaza el HttpServletResponse#getWriter() con una implementación propia que copia el cuerpo de la respuesta en un búfer. Después de continuar la cadena de filtro con la respuesta reemplazada, solo registre la copia.

Aquí está un ejemplo patada de cómo el método doFilter() puede verse como:

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

Así es como el CopyPrintWriter puede verse como:

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

} 

Ubicación del filtro en una url-pattern para el que desea para registrar respuestas para. Tenga en cuenta que el contenido binario/estático como imágenes, CSS, archivos JS, etc. no se registrará de esta manera. Desea excluirlos mediante el uso de un número suficientemente específico url-pattern, p. Ej. *.jsp o simplemente en el servlet-name del servlet en cuestión. Si desea registrar contenido binario/estático de todos modos (para lo cual no veo ningún beneficio), entonces necesita reemplazar el HttpServletResponse#getOutputStream() de la misma manera también.

+0

Debe reemplazar no solo 'getWriter()', pero también 'getOutputStream()' – pihentagy

+0

¿Qué hay de los problemas de codificación? – pihentagy

+0

@pihentagy: 1) Si es necesario. 2) Use lo mismo que se establece mediante el método 'setCharacterEncoding()'. – BalusC

4

Tal vez un servlet filter puede ayudarlo. Piense en ello como una programación orientada a aspectos para HTTP.

+2

Es curioso cómo cada otra respuesta cita la mía, pero no fue la aceptada ... – duffymo

+6

Fue una sugerencia útil realmente (votada), pero pierde la pista acerca de cómo copiar el 'PrintWriter' – pihentagy

3

Una alternativa a BalusC answer Usando el TeeOutputStream para escribir en dos salidas en el tiempo.

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

Para el beneficio de las generaciones futuras ... ¿de qué bibliotecas son 'TeeOutputStream' y' DelegatingServletOutputStream'? –

+0

Es de primavera y apache io. 1) https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/mock/web/DelegatingServletOutputStream.html 2) https://commons.apache.org/proper/commons-io /javadocs/api-1.4/org/apache/commons/io/output/TeeOutputStream.html – Happier

-1

Realizo una versión OutputStream sin dependencia de terceros, similar con @pdorgambide. Puede encontrarlo en this link.

+0

Las respuestas del enlace solo son malas ... los enlaces finalmente se rompen. – Gus

+0

¿Puedes abrir esta pregunta? http://stackoverflow.com/questions/10457963/spring-rest-service-retrieving-json-from-request/29005111#29005111 – Happier

Cuestiones relacionadas