Mark
Mark

Reputation: 4706

Java Servlet Filter - Modify response headers based on status

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?

Upvotes: 4

Views: 6424

Answers (4)

DwB
DwB

Reputation: 38290

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.

Upvotes: 0

afrish
afrish

Reputation: 3305

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

Upvotes: 0

user207421
user207421

Reputation: 310860

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

Upvotes: 0

Mark
Mark

Reputation: 4706

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.

Upvotes: 2

Related Questions