4

I'm trying to write a servlet filter that will add headers to the response depending on the status of the request. I know I have to wrap the response with a HttpServletResponseWrapper before passing to the chain.doFilter but the headers never get sent, so I'm obviously missing something very obvious.

Code looks something like:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
   HttpServletResponse httpServletResponse = (HttpServletResponse) response;
   HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(httpServletResponse);

   chain.doFilter(request, responseWrapper);

   if(responseWrapper.getStatus() < 400)
   {
      responseWrapper.addHeader("X-Custom-Foobar", "abc");
   }
}

Is there something I have to capture in the wrapper to prevent the response from going out to the client until the check is complete?

Mark
  • 4,446
  • 5
  • 26
  • 34

4 Answers4

2

So the frustrating part about this spec is that you have to completely intercept the ServletOutputStream and buffer it. I ended up following the example here :: https://stackoverflow.com/a/11027170/76343

The base class HttpServletResponseWrapper is a complete passthrough and as soon as the output stream is closed, all further modifications to the response are mute.

Unfortunately there doesn't seem to be a more elegant way to accomplish this.

Community
  • 1
  • 1
Mark
  • 4,446
  • 5
  • 26
  • 34
0

You need to extend HttpResponseWrapper() and override the appropriate methods. Just using a vanilla HttpResponseWrapper by itself accomplishes exactly nothing.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • @downvoter It accomplishes something? Such as what? How? Even though it is a complete pass through, as the other answer notes? Public has a right to know. – user207421 Mar 02 '20 at 09:50
0

This is actually possible. But because after calling chain.doFilter(request, response) the response is already committed, we have to set the headers after receiving the status code, but before the response is committed. Here is an example:

public class HeadersFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
                                                                                             ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        chain.doFilter(request, new ResponseWrapper(response));
    }

    public static class ResponseWrapper extends HttpServletResponseWrapper {
        public ResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public void setStatus(int sc) {
            super.setStatus(sc);

            // SET YOUR HEADERS HERE
            // setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        }
    }

    @Override
    public void destroy() {
    }
}
afrish
  • 3,167
  • 4
  • 30
  • 38
  • Have you actually run this? `setStatus(int sc)` isn't ever called. So no headers can be set. – Paul Uszak Jan 08 '22 at 15:58
  • @PaulUszak Not only have I run this, but I have code in production very similar to this. If `setStatus(int sc)` is not called for you, how do you suppose response gets returned without any status? – afrish Jan 10 '22 at 11:23
  • Sorry mate, I put a print statement below `super.setStatus(sc);` and it is not called on my machine. The wrapper is created, but the method isn't called. If you then add wacky custom headers, they are not added. I'm running Tomcat 9. Doesn't matter as I've resolved it another way :-) – Paul Uszak Jan 10 '22 at 12:38
0

The order of this code is inverted:

   chain.doFilter(request, responseWrapper);

   if(responseWrapper.getStatus() < 400)
   {
      responseWrapper.addHeader("X-Custom-Foobar", "abc");
   }

Try this instead:

   if(responseWrapper.getStatus() < 400)
   {
      responseWrapper.addHeader("X-Custom-Foobar", "abc");
   }

   chain.doFilter(request, responseWrapper);

The doFilter method does not return to your method until after the response has been sent on the wire.

DwB
  • 37,124
  • 11
  • 56
  • 82