关于java:查找使用servlet过滤器将内容插入响应的示例

Looking for an example for inserting content into the response using a servlet filter

我一直在搜索net和stackoverflow,寻找一个使用servlet过滤器将内容插入响应的示例,但只能找到捕获/压缩输出和/或更改头的示例。我的目标是在所有HTML响应的结束&60;/body&62;之前附加一块HTML。

我正在开发一个扩展httpservletresponsewrapper的解决方案,以使用我自己的printwriter,然后覆盖上面的写入方法。在write方法中,我将存储最后7个字符,以查看它是否等于结束体标记,然后在继续对文档其余部分进行正常写入操作之前,先编写HTML块和结束体标记。

我觉得一定有人已经解决了这个问题,也许比我更优雅。对于如何使用servlet过滤器将内容插入响应的任何示例,我都表示感谢。

更新的

在回应评论时,我还试图从http://www.oracle.com/technetwork/java/filters-137243.html实现charresponsewrapper。这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();

我也在包装请求,但是代码可以工作,不应该影响响应。


我使用的代码基在处理响应时调用getOutputStream方法,而不是getWriter,因此其他答案中包含的示例没有帮助。这里有一个更完整的答案,它同时适用于outputstream和printwriter,即使在两次访问writer时出错也是正确的。这是从使用javax.servlet.filter的转储请求和响应这个很好的例子中派生出来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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);
        }
    }
}


您需要实现httpservletresponsewrapper来修改响应。请参阅本文档过滤器的要点,它有一个转换响应的示例,这比您想要的要多

编辑

我尝试过一个简单的带有响应过滤器的servlet,它工作得很好。servlet输出字符串Test和响应过滤器,将字符串filtered附加到该字符串上,最后,当我从浏览器运行时,会得到响应Test filtered,这正是您想要实现的。

我在ApacheTomcat7上运行了下面的代码,它毫无例外地工作。

Servlet:

1
2
3
4
5
6
protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

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

}

过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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(与文章完全相同)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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>


ITECH的回答对我有部分作用,这是基于那个回答。

但您必须注意,似乎有些Web服务器(和AppEngine标准)在过滤器内部第一次调用chain.dofilter后关闭输出流。

因此,当您需要在预先保存的printwriter上写入时,流将关闭,您将得到一个空白屏幕。(我甚至没有错误地意识到发生了什么)。

所以我的解决方案是创建一个"虚拟"servletoutputstream,并返回到responsewrapper的getoutputstream方法中。

这些更改加上ITECH的解决方案允许我在JSON响应中插入HTML中完全呈现的JSP响应(正确地转义引号之类的冲突字符)。

这是我的代码:

My滤波器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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() {}
}

我的责任人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
            }
        };
    }
}