Search code examples
javahttpfilterinterceptormicronaut

Why micronaut intercepts requests in reversed order


I'm quite new to the reactive world, right now I'm trying to create two HttpServerFilters that will map the response in server written in micronaut. To present my issue I prepared simplified code snipped:

@Filter("/**")
public class FirstFilter implements HttpServerFilter {

    @Override
    public int getOrder(){
        return ServerFilterPhase.FIRST.before();
    }

    @Override
    public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
        System.out.println("Returning publisher in FirstFilter!");
        return Publishers.then(
                chain.proceed(request),
                mutableHttpResponse -> {
                    System.out.println("Mapping response in first filter!");
                    mutableHttpResponse.getBody(ServiceHttpResponse.class)
                            .ifPresent(resp -> mutableHttpResponse.status(resp.getStatus()));
                });
    }
}
@Filter("/**")
public class SecondFilter implements HttpServerFilter {

    @Override
    public int getOrder(){
        return ServerFilterPhase.FIRST.after();
    }

    @Override
    public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
        System.out.println("Returning publisher in SecondFilter!");
        return Publishers.then(
                chain.proceed(request),
                mutableHttpResponse -> {
                    System.out.println("Mapping response in second filter!");
                    mutableHttpResponse.getBody(ServiceHttpResponse.class)
                            .ifPresent(resp -> mutableHttpResponse.body(resp.format()));
                });
    }
}

logs after executing a request are as follow:

Returning publisher in FirstFilter!
Returning publisher in SecondFilter!
Mapping response in second filter!
Mapping response in first filter!

From logs i can see that doFilter methods are invoked in the right order, but the methods in reactive stream that maps the response are invoked in reversed.

In my example I'm trying to first set response status using information from current response body (ServiceHttpResponse) and then seta new, formatted body but because these executes in reversed order, new body is set first and information about response status is lost.

What is causing this behavior? I tested it using more that 2 filters and the order is always reversed, so it's not random.


Solution

  • As mentioned by Jon Skeet, this is a filter chain. In this chain you have all your filters in the given order. After the chain reaches the end the actual controller is called. And the the same filter chain is processed in the reverse order.

    @Override
    public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
            // This happens before the controller gets invoked
            System.out.println("Returning publisher in SecondFilter!");
            return Publishers.then(
                    chain.proceed(request),
                    mutableHttpResponse -> {
                        // this happens after the controller has been invokes. That's why you have the http response available.
                        System.out.println("Mapping response in second filter!");
                        mutableHttpResponse.getBody(ServiceHttpResponse.class)
                                .ifPresent(resp -> mutableHttpResponse.body(resp.format()));
            });
    }
    

    The following image illustrates the pattern. It's not 1:1 what Micronaut does but it should give you an impression how things work.

    enter image description here

    Image source: https://www.informit.com/articles/article.aspx?p=1398619&seqNum=3