1

I'm coming from Node-ExpressJS, so I am familiar with the concept of middlewares. As I was learning Spring, I came to know of a component called Filter which pretty much acts like middlewares in Express with a few differences.

So I'm trying to understand how a Filter and FilterChain actually works in Spring.

I have the following code:

Filter1.java

@Component
@Order(1)
public class Filter1 implements Filter {

    .....
    .....

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

        LOGGER.info("############# Invoking Filter1 ############");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        LOGGER.info("************ Moving on to next Filter");
        LOGGER.info("Adding new Attribute");
        req.setAttribute("Custom_Attribute_1", "TEST***TEST***TEST");
        chain.doFilter(request, response);
        resp.addHeader("1st Header", "1ST"); // Custom header that never shows up

        LOGGER.info("+++++++++ GOING BACK FROM Filter1 +++++++++");

    }

}

Filter2.java

@Component
@Order(2)
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        LOGGER.info("############# Invoking Filter2 ####################");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        req.setAttribute("Filter2 Attribute", "2nd ORDER");
        resp.addHeader("2nd Header", "2ND"); //Custom header that actually shows up
        chain.doFilter(request, response);      
        LOGGER.info("+++++++++++ GOING BACK FROM Filter2 ++++++++++");
    }

}

Controller.java

@RestController
public class Controller {

   @GetMapping("/")
   public ResponseEntity<Object> createResource(HttpServletRequest req) {
     return new ResponseEntity<Object>("Resource Created",HttpStatus.OK);
   }
}

When I send a request to my controller using Postman, I can see only one of my custom headers in the response, namely the 2nd Header, but can't see the other header.

Headers of response in Postman

2nd Header → 2ND
Content-Type → text/plain;charset=UTF-8
Content-Length → 15
Date → Thu, 19 Sep 2019 20:16:25 GMT

Does the call of chain.doFilter(request, response) have anything to do with it? It seems like there can be no modification to the response object in Filter1 class once doFilter of FilterChain has been called.

What I am trying to understand here is:

  1. If FilterChain.doFilter is what needs to be called to propagate the request object to the next filters and eventually to the controller, shouldn't the response object be allowed to be modified once the call to chain.doFilter returns? How exactly does it work internally? How does the call propagate all the down to the controller and then come back up to the first filter?

  2. Also, if Filter1 wanted to see the body of the response after it returned from Filter2 and possibly modify it, how would it do so?

Auro
  • 1,578
  • 3
  • 31
  • 62
  • 1
    There's no magic here - the call to `doFilter` is _blocking_. When it returns (either normally or exceptionally) the downstream filters and the request processors themselves are done. Think of it more like Russian nesting dolls than a chain. – Boris the Spider Sep 19 '19 at 20:46
  • The reason your code doesn't work is exactly related to this - once `doFilter` returns the main request processor has completed - it has most likely flushed the stream and sent content. You cannot send more headers once the header has already been sent - there is no pass by reference over TCP! If you want to add headers after, you'd need to buffer the entire response - which might be very costly or even impossible. – Boris the Spider Sep 19 '19 at 20:49

1 Answers1

0

Basicly, doFilter() sends request and response object to next Filter in FilterChain as mentioned in javadoc.

In your first example, before adding "1st Header" you invoke to next filter in tha chain. That's why you don't get "1st Header" in the first place. This request will go Controller layer and then get evaluated by your controller. When the controller is done with the object, response starts populating to back to filters.

So your code works like this

arrived Filter1 -> Filter 2 > add "2st Header" > .. > Controller > Controller runs and prepares a response object.

Controller Response > .... > Filter 2 > Filter 1 > add "1st Header" > ...

So when request reaches to your controller, the controller will never have "1st Header" in the request context.

Also, if Filter1 wanted to see the body of the response after it returned from Filter2 and possibly modify it, how would it do so?

I have not tried that but you should have a look to this thread, it looks like what you want to achieve.