web-dev-qa-db-fra.com

Capturez et enregistrez le corps de la réponse

J'ai un servlet qui gère certaines demandes et réponses HTTP. Je souhaite enregistrer le corps de la réponse avant de le renvoyer au client. Existe-t-il un moyen de capturer le corps de la réponse avant son envoi en tant qu'objet HttpServletResponse à partir de la servlet?

28
Jony

Si je vous comprends bien, vous voulez enregistrer la réponse body ? C'est une tâche assez coûteuse, mais si c'est l'exigence commerciale ...

Comme l'a souligné @duffymo, un Filter est un endroit approprié pour cela. Vous pouvez capturer le corps de la réponse en remplaçant l'implémentation ServletResponse transmise par une implémentation HttpServletResponseWrapper qui remplace la fonction HttpServletResponse#getWriter() par une implémentation propre qui copie le corps de réponse dans un tampon. Après avoir poursuivi la chaîne de filtrage avec la réponse remplacée, enregistrez simplement la copie.

Voici un exemple de lancement à quoi peut ressembler la méthode 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());
}

Voici à quoi peut ressembler le 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();
    }

}

Mappez ce filtre sur un url-pattern Pour lequel vous souhaitez enregistrer les réponses. Gardez à l'esprit que le contenu binaire/statique comme les images, les fichiers CSS, JS, etc. ne sera pas enregistré de cette façon. Vous souhaitez les exclure en utilisant un url-pattern Suffisamment spécifique, par exemple *.jsp Ou simplement sur le servlet-name De la servlet en question. Si vous voulez quand même enregistrer du contenu binaire/statique (pour lequel je ne vois aucun avantage), vous devez également remplacer la HttpServletResponse#getOutputStream() de la même manière.

29
BalusC

Une alternative à réponse BalusC Utilisation de TeeOutputStream pour écrire dans deux flux de sortie à la fois.

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

Peut-être qu'un filtre de servlet peut vous aider. Considérez-le comme une programmation orientée aspect pour HTTP.

3
duffymo