2010-07-13 23 views
19

Ho un servlet che gestisce determinate richieste e risposte HTTP. Voglio registrare il corpo della risposta prima di inviarlo al cliente. Esiste un modo per acquisire il corpo della risposta prima che venga inviato come oggetto HttpServletResponse dal servlet?Cattura e registra il corpo della risposta

risposta

25

Se ho capito bene, si desidera registrare la risposta corpo? Questo è un compito piuttosto costoso, ma se questo è il requisito aziendale ...

Come indicato da @duffymo, un posto adatto per questo è un Filter. È possibile acquisire il corpo della risposta sostituendo l'implementazione ServletResponse con un'implementazione HttpServletResponseWrapper che sostituisce lo HttpServletResponse#getWriter() con una propria implementazione che copia il corpo della risposta in un buffer. Dopo aver proseguito con la catena di filtri con la risposta sostituita, è sufficiente registrare la copia.

Ecco un esempio di calcio d'inizio come il metodo doFilter() può apparire come:

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

Ecco come il CopyPrintWriter può apparire come:

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

} 

Mappa del filtro su un url-pattern per il quale vuoi per registrare le risposte per. Tieni presente che i contenuti binari/statici come immagini, CSS, file JS e così via non verranno registrati in questo modo. Vorresti escluderli utilizzando uno specifico url-pattern, ad es. *.jsp o solo sul servlet-name del servlet in questione. Se si desidera comunque registrare il contenuto binario/statico (per il quale non vedo alcun beneficio), è necessario sostituire lo HttpServletResponse#getOutputStream() allo stesso modo.

+0

È necessario eseguire l'override non solo 'getWriter()', ma anche 'getOutputStream()' – pihentagy

+0

E i problemi di codifica? – pihentagy

+0

@pihentagy: 1) Se è necessario. 2) Usa lo stesso che è impostato dal metodo 'setCharacterEncoding()'. – BalusC

4

Forse un servlet filter può aiutarti. Pensa a una programmazione orientata agli aspetti per HTTP.

+2

Divertente come ogni altra risposta cita la mia, ma non era quella accettata .... – duffymo

+6

È stato un suggerimento utile davvero (ingrandito), ma manca l'indizio sulla copia di 'PrintWriter' – pihentagy

3

Un'alternativa a BalusC answer Utilizzo di TeeOutputStream per scrivere in due flussi di output alla volta.

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

Per il beneficio delle generazioni future ... quali librerie sono 'TeeOutputStream' e' DelegatingServletOutputStream'? –

+0

È dalla primavera e 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

Realizzo una versione di OutputStream senza dipendenza di terze parti, simile a @pdorgambide. È possibile trovare in this link.

+0

Le sole risposte di collegamento sono cattive ... i collegamenti alla fine si interrompono. – Gus

+0

Puoi aprire questa domanda? http://stackoverflow.com/questions/10457963/spring-rest-service-retrieving-json-from-request/29005111#29005111 – Happier

Problemi correlati