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?
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.
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();
}
Peut-être qu'un filtre de servlet peut vous aider. Considérez-le comme une programmation orientée aspect pour HTTP.