Looking for an example for inserting content into the response using a servlet filter
The codebase I am using, calls the getOutputStream method, instead of getWriter when it processes the response, so the examples included in the other answer doesn't help. Here is a more complete answer that works with both the OutputStream and the PrintWriter, even erroring correctly, if the writer is accessed twice. This is derived from the great example, 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);
}
}
}
You will need to implement HttpServletResponseWrapper to modify the response. See this document The Essentials of Filters, it has an example that transforms the response, which is more than what you want
Edit
I have tried a simple Servlet with response filter and it worked perfectly. The Servlet output the string Test
and the response filter append to it the string filtered
and finally when I run from the browser I get the response Test filtered
which is what you are trying to achieve.
I did run the below code on Apache Tomcat 7 and it is working without exceptions.
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 (exactly as the 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>
The iTech answer worked partially for me and this is based on that response..
But you must notice, that it seems some web servers (and AppEngine Standard) closes the outputStream after the first call to chain.doFilter inside a Filter..
So when you need to write on the pre-saved PrintWritter, the stream is closed and you get a blank screen. (I didn't recieve even an error to realise what was happening).
So the solution for me was creating a "dummy" ServletOutputStream and returning back in the getOutputStream method of my ResponseWrapper.
These changes plus the solution of iTech allowed me to insert a fully rendered jsp response in html inside a json response (properly escaping conflictive characters like quotes).
This is my 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() {}
}
My 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;
}
};
}
}