web-dev-qa-db-fra.com

Recherche d'un exemple pour insérer du contenu dans la réponse à l'aide d'un filtre de servlet

J'ai cherché sur le net et stackoverflow un exemple de quelqu'un insérant du contenu dans la réponse à l'aide d'un filtre de servlet, mais je ne peux trouver que des exemples de personnes capturant/compressant la sortie et/ou changeant les en-têtes. Mon objectif est d'ajouter un morceau de HTML juste avant la fermeture </body> de toutes les réponses HTML.

Je travaille sur une solution qui étend le HttpServletResponseWrapper pour utiliser mon propre PrintWriter, puis en remplaçant les méthodes d'écriture sur celui-ci. À l'intérieur de la méthode d'écriture, je stocke les 7 derniers caractères pour voir si elle est égale à la balise de fermeture du corps, puis j'écris mon morceau HTML plus la balise de fermeture du corps, avant de poursuivre les opérations d'écriture normales pour le reste du document.

Je pense que quelqu'un a déjà résolu ce problème, et probablement plus élégamment que moi. J'apprécierais tous les exemples d'utilisation d'un filtre de servlet pour insérer du contenu dans une réponse.

MIS À JOUR

En réponse à un commentaire, j'essaie également d'implémenter CharResponseWrapper à partir de http://www.Oracle.com/technetwork/Java/filters-137243.html . Voici mon code:

PrintWriter out = response.getWriter();
CharResponseWrapper wrappedResponse = new CharResponseWrapper(
        (HttpServletResponse)response);

chain.doFilter(wrappedRequest, wrappedResponse);
String s = wrappedResponse.toString();

if (wrappedResponse.getContentType().equals("text/html") &&
        StringUtils.isNotBlank(s)) {
    CharArrayWriter caw = new CharArrayWriter();
    caw.write(s.substring(0, s.indexOf("</body>") - 1));
    caw.write("WTF</body></html>");
    response.setContentLength(caw.toString().length());
    out.write(caw.toString());
}
else {
    out.write(wrappedResponse.toString());
}

out.close();

J'encapsule également la demande, mais ce code fonctionne et ne devrait pas affecter la réponse.

32
matt snider

La base de code que j'utilise appelle la méthode getOutputStream, au lieu de getWriter lorsqu'elle traite la réponse, de sorte que les exemples inclus dans l'autre réponse n'aident pas. Voici une réponse plus complète qui fonctionne à la fois avec OutputStream et PrintWriter, même en cas d'erreur correctement, si l'écrivain est accédé deux fois. Ceci est dérivé du grand exemple, DUMP REQUEST AND RESPONSE USING JAVAX.SERVLET.FILTER .

import javax.servlet.*;
import javax.servlet.http.*;
import Java.io.*;

public class MyFilter implements Filter
{
    private FilterConfig filterConfig = null;

    private static class ByteArrayServletStream extends ServletOutputStream
    {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos)
        {
            this.baos = baos;
        }

        public void write(int param) throws IOException
        {
            baos.write(param);
        }
    }

    private static class ByteArrayPrintWriter
    {

        private ByteArrayOutputStream baos = new ByteArrayOutputStream();

        private PrintWriter pw = new PrintWriter(baos);

        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter()
        {
            return pw;
        }

        public ServletOutputStream getStream()
        {
            return sos;
        }

        byte[] toByteArray()
        {
            return baos.toByteArray();
        }
    }

    public class CharResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayPrintWriter output;
        private boolean usingWriter;

        public CharResponseWrapper(HttpServletResponse response)
        {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray()
        {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException
        {
            // will error out, if in use
            if (usingWriter) {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString()
        {
            return output.toString();
        }
    }

    public void init(FilterConfig filterConfig) throws ServletException
    {
        this.filterConfig = filterConfig;
    }

    public void destroy()
    {
        filterConfig = null;
    }

    public void doFilter(
            ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        CharResponseWrapper wrappedResponse = new CharResponseWrapper(
                (HttpServletResponse)response);

        chain.doFilter(request, wrappedResponse);
        byte[] bytes = wrappedResponse.getByteArray();

        if (wrappedResponse.getContentType().contains("text/html")) {
            String out = new String(bytes);
            // DO YOUR REPLACEMENTS HERE
            out = out.replace("</head>", "WTF</head>");
            response.getOutputStream().write(out.getBytes());
        }
        else {
            response.getOutputStream().write(bytes);
        }
    }
}
42
matt snider

Vous devrez implémenter HttpServletResponseWrapper pour modifier la réponse. Voir ce document L'essentiel des filtres , il a un exemple qui transforme la réponse, ce qui est plus que ce que vous voulez

Modifier

J'ai essayé un simple servlet avec filtre de réponse et cela a parfaitement fonctionné. Le servlet affiche la chaîne Test et le filtre de réponse lui ajoute la chaîne filtered et enfin lorsque je cours depuis le navigateur, j'obtiens la réponse Test filtered c'est ce que vous essayez de réaliser.

J'ai exécuté le code ci-dessous sur Apache Tomcat 7 et cela fonctionne sans exception.

Servlet:

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

   response.getWriter().println("Test");

}

Filtre:

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    System.out.println("BEFORE filter");
    PrintWriter out = response.getWriter();
    CharResponseWrapper responseWrapper = new CharResponseWrapper(
            (HttpServletResponse) response);

    chain.doFilter(request, responseWrapper);

    String servletResponse = new String(responseWrapper.toString());

    out.write(servletResponse + " filtered"); // Here you can change the response


    System.out.println("AFTER filter, original response: "
            + servletResponse);

}

CharResponseWrapper (exactement comme l'article)

public class CharResponseWrapper extends HttpServletResponseWrapper {
    private CharArrayWriter output;

    public String toString() {
        return output.toString();
    }

    public CharResponseWrapper(HttpServletResponse response) {
        super(response);
        output = new CharArrayWriter();
    }

    public PrintWriter getWriter() {
        return new PrintWriter(output);
    }
}

web.xml

<servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>TestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/TestServlet</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>MyFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/TestServlet/*</url-pattern>
</filter-mapping>
18
iTech

La réponse iTech a fonctionné en partie pour moi et cela est basé sur cette réponse ..

Mais vous devez remarquer qu'il semble certains serveurs Web (et AppEngine Standard) ferme le outputStream après le premier appel à chain.doFilter à l'intérieur d'un filtre.

Ainsi, lorsque vous devez écrire sur le PrintWritter préenregistré, le flux est fermé et vous obtenez un écran vide. (Je n'ai même pas reçu d'erreur pour réaliser ce qui se passait).

La solution pour moi consistait donc à créer un "factice" ServletOutputStream et à revenir dans la méthode getOutputStream de mon ResponseWrapper.

Ces changements ainsi que la solution d'iTech m'ont permis d'insérer une réponse jsp entièrement rendue en html dans une réponse json (en échappant correctement les caractères conflictuels comme les guillemets).

Voici mon code:

Myfilter

@WebFilter({"/json/*"})    
public class Myfilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //Save original writer
        PrintWriter out = response.getWriter(); 
        //Generate a response wrapper with a different output stream
        ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
        //Process all in the chain (=get the jsp response..)
        chain.doFilter(request, responseWrapper);
        //Parse the response
        out.write("BEFORE"+responseWrapper.toString()+"AFTER"); //Just + for clear display, better use a StringUtils.concat
    }
    @Override
    public void destroy() {}
}

Mon ResponseWrapper:

public class ResponseWrapper extends HttpServletResponseWrapper {
    private StringWriter output;
    public String toString() {
        return output.toString();
    }
    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        //This creates a new writer to prevent the old one to be closed
        output = new StringWriter();
    }
    public PrintWriter getWriter() {
        return new PrintWriter(output,false);
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        //This is the magic to prevent closing stream, create a "virtual" stream that does nothing..
        return new ServletOutputStream() {
            @Override
            public void write(int b) throws IOException {}
            @Override
            public void setWriteListener(WriteListener writeListener) {}
            @Override
            public boolean isReady() {
                return true;
            }
        };
    }
}
4
Xarly CR

Génial! mais veuillez mettre à jour la longueur du contenu,

        String out = new String(bytes);
        // DO YOUR REPLACEMENTS HERE
        out = out.replace("</head>", "WTF</head>");
        response.setContentLength(out.length());
        response.getOutputStream().write(out.getBytes());
0
Adam Gong