Java filter failing to set response headers

27,916

Solution 1

I think your issue is related to the Filter order of execution of your Web Context, i.e. some filters, in you web context, executes after your filter and override the header.

The Servlet Filter is an implementation of the Chain of Responsibility pattern

So you may try to:

  • Set the headers after the call to chain.doFilter:

.

...

chain.doFilter(req,res);

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

//get the headers we placed in the request
//based on those request headers, set some response headers

if(req.getHeader("X-Wria-Download") != null){
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", "attachment; filename=success.pdf");
}

In this way your code will be executed after that the Servlet is called and, as explained below, if your filter is the first declared in web.xml, then the setHeader code will be the last executed (see image below).

  • make sure that your filter is the last to be executed after the Servlet is executed, i.e. it should be the first servlet Filter declared as explained here:

enter image description here

As you can see Filter1 (the first declared in web.xml) is the first one executed before the servlet is executed and the last one executed after the servlet is executed. So if you want to be sure to be the last Filter setting the header then declare it as Filter1.

The order of execution is determined by the order of declaration in your Deployment Descriptor (web.xml):

Servlet spec (section 6.2.4):

"The order the container uses in building the chain of filters to be applied for a particular request URI is as follows:

"1. First, the matching filter mappings in the same order that these elements appear in the deployment descriptor.

"2. Next, the matching filter mappings in the same order that these elements appear in the deployment descriptor."

So to be sure simply declare it as the first filter in your web.xml. In this way it will be the very last filter setting the header. And, of course, set the header in your code after calling chain.doFilter, as already said.

Solution 2

Assuming you use a response wrapper as described here by others, the whole secret is when to call getWriter() on the original response! That's because the response object ignores all headers added AFTER you asked for a writer!

So, make sure you add all your headers Before you call getWriter(). Here is my recommended sequence for doFilter():

  1. Create a response wrapper

  2. chain.doFilter (origRequest, wrapper);

  3. Assign all required headers to the original (!) Response

  4. Get writer from original response

  5. Copy the wrapper's content to this writer

Share:
27,916
Sol Slay
Author by

Sol Slay

Updated on September 06, 2020

Comments

  • Sol Slay
    Sol Slay over 3 years

    I am trying to create a Java "Filter" which detects a custom HTTP Request Header, and inserts response headers so that the file will download automatically. The response header that is most important for this is the "Content-Type = Attachment" response header. I have created an HTTP request object that inserts the custom Header:

    function myHttpObject(filePath){
    function makeHttpObject() {
        return new XMLHttpRequest();
    }
    
    var request = makeHttpObject();
    
    request.open("GET", filePath, false);
    request.setRequestHeader("X-Wria-Download", "PDFdownload");
    request.send(null);
    window.open(filePath);
    console.log(request.getAllResponseHeaders());
    }
    

    This will insert the X-Wria-Download header into the request. Then I have a Java Filter which looks for that request header and should set the response header to "Content-Type=attachment"

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    public class Contenttypefilter implements Filter  {
    
    protected FilterConfig filterConfig;
    
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
    
    public void destroy() {
        //noop
    }
    
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
    
        //get the headers we placed in the request
        //based on those request headers, set some response headers
    
        if(req.getHeader("X-Wria-Download") != null){
            res.setHeader("Content-Type", "application/pdf");
            res.setHeader("Content-Disposition", "attachment; filename=success.pdf");
        }
    
        chain.doFilter(req,res);
    }
    
    
    }
    

    And then of course the web.xml has the code to include the Filter on all jsp files.

    The thing that is baffling me, is that the header is being set on the response file, but it is not downloading as it should. If I put the res.setHeader("Content-Disposition", "attachment; filename=success.pdf"); line outside the "if" statement, then it will work, but it will apply the download behavior to all JSP's which I don't want.

    Why is it applying the content-disposition but not working when I have the res.setHeader in the if statement; and then working when it is outside the if statement? Any ideas for how I can get the desired behavior (only applying content disposition to jsp's that I have applied a custom request header to)?

  • Sol Slay
    Sol Slay over 11 years
    The header(X-Wria-Download) actually does seem to be getting applied to the request object before being passed to the java Filter. When going through the Filter in debug mode it detects the custom request header, and subsequently executes the if statement above: if(req.getHeader("X-Wria-Download") != null){}.
  • Sol Slay
    Sol Slay over 11 years
    Ok so I've tried doing this. For some reason, after the chain.doFilter, the content-type always reverts back to text/html.
  • Brad Parks
    Brad Parks about 10 years
    thanks! the key things that helped me from this is that 1) Make sure your filter is added first, so it will get called last, and 2) make sure you modify the response after calling chain.doFilter(req,res), not before, or your response may get overwritten.
  • Dawngerpony
    Dawngerpony over 8 years
    Unfortunately this didn't work for me using Dropwizard 0.6 / Jetty 8. (I'm trying to modify cookie values.) For some reason, no matter where I add the chain.doFilter() call, my response comes back with the original values of the headers. I've had to solve my problem in a different, less desirable way, but I'm just commenting on this answer in case anyone else has had issues too.
  • Sky
    Sky almost 8 years
    Thanks for the good description. I've tried to do such a filter with Jetty 9.2 but unfortunately it also didn't work, seems to be the same issue as @DuffJ had. In my case the filter worked as expected only if I declared it as last filter in my web.xml.