wake-up-neo.net

Suchen Sie nach einem Beispiel zum Einfügen von Inhalten in die Antwort mithilfe eines Servlet-Filters

Ich habe das Netz und den Stackoverflow nach einem Beispiel durchsucht, in dem jemand mithilfe eines Servlet-Filters Inhalt in die Antwort einfügt, kann aber nur Beispiele von Personen finden, die die Ausgabe erfassen/komprimieren und/oder die Header ändern. Mein Ziel ist es, einen Teil des HTML-Codes kurz vor dem Abschluss </ body> aller HTML-Antworten anzufügen.

Ich arbeite an einer Lösung, die den HttpServletResponseWrapper erweitert, um meinen eigenen PrintWriter zu verwenden, und dann die darauf befindlichen Schreibmethoden überschreibt. Innerhalb der Schreibmethode speichere ich die letzten 7 Zeichen, um zu prüfen, ob sie dem schließenden Body-Tag entsprechen, und schreibe dann meinen HTML-Block plus dem schließenden Body-Tag, bevor ich den normalen Schreibvorgang für den Rest des Dokuments fortsetze.

Ich habe das Gefühl, dass jemand dieses Problem bereits gelöst haben muss, und wahrscheinlich eleganter als ich. Ich würde mich über Beispiele freuen, wie man einen Servlet-Filter verwendet, um Inhalt in eine Antwort einzufügen.

AKTUALISIERT

Als Antwort auf einen Kommentar versuche ich auch, den CharResponseWrapper von http://www.Oracle.com/technetwork/Java/filters-137243.html zu implementieren. Hier ist mein 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();

Ich wickle auch die Anfrage ein, aber dieser Code funktioniert und sollte die Antwort nicht beeinflussen.

32
matt snider

Die von mir verwendete Codebasis ruft bei der Verarbeitung der Antwort die Methode getOutputStream anstelle von getWriter auf, sodass die in der anderen Antwort enthaltenen Beispiele nicht hilfreich sind. Hier ist eine vollständigere Antwort, die sowohl mit dem OutputStream als auch mit dem PrintWriter funktioniert, auch wenn ein Fehler vorliegt, wenn auf den Writer zweimal zugegriffen wird. Dies ergibt sich aus dem großartigen Beispiel 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

Sie müssen HttpServletResponseWrapper implementieren, um die Antwort zu ändern. Siehe dieses Dokument Die Grundlagen von Filtern , es gibt ein Beispiel, das die Antwort umwandelt. Das ist mehr als das, was Sie wollen

Bearbeiten

Ich habe ein einfaches Servlet mit Antwortfilter ausprobiert und es hat einwandfrei funktioniert. Das Servlet gibt die Zeichenfolge Test aus und der Antwortfilter fügt die Zeichenfolge filtered hinzu. Wenn ich vom Browser aus starte, erhalte ich die Antwort Test filtered was ist das, was Sie erreichen wollen.

Ich habe den folgenden Code auf Apache Tomcat 7 ausgeführt und er funktioniert ohne Ausnahmen.

Servlet:

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

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

}

Filter:

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 (genau wie der Artikel)

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

Die iTech-Antwort hat teilweise bei mir funktioniert und basiert auf dieser Antwort.

Aber Sie müssen feststellen, dass es scheint, einige Webserver (und AppEngine Standard) schließt den outputStream nach dem ersten Aufruf von chain.doFilter innerhalb eines Filters.

Wenn Sie also auf den gespeicherten PrintWritter schreiben müssen, wird der Stream geschlossen und Sie erhalten einen leeren Bildschirm. (Ich habe nicht einmal einen Fehler erhalten, um zu erkennen, was passierte).

Die Lösung für mich war also, einen "Dummy" ServletOutputStream zu erstellen und in die getOutputStream-Methode meines ResponseWrapper zurückzukehren.

Diese Änderungen und die Lösung von iTech ermöglichten es mir, eine vollständig gerenderte JSP-Antwort in HTML in eine JSON-Antwort einzufügen (wobei konfliktträchtige Zeichen wie Anführungszeichen ordnungsgemäß ausgeblendet werden).

Das ist mein 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() {}
}

Mein 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

Groß! aber bitte inhaltliche Länge aktualisieren,

        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