Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring Cloud Gateway MVC, order of before and after functions. #3387

Open
gatewaynovice opened this issue May 8, 2024 · 6 comments
Open

Comments

@gatewaynovice
Copy link

gatewaynovice commented May 8, 2024

Question

I'm using Spring Cloud Gateway MVC to create custom routes. I need to associate multiple before functions and multiple after functions for each route I build. I needed to control the order of execution of these before and after function. I was assuming the order of functions in the route definition would also be the execution order of these functions. In practice during my testing I'm seeing the "before" functions are execution follow the order they are defined in but the "after functions execution is in the reverser order.

Is there anything I can do to control the order?

Example:

route(routeId)
        .nest(
            path("customPath/*"),
            () ->
                route()
                    .before(beforeFunction1)
                    .before(beforeFunction2)
                    .before(beforeFunction3)
                    .route(all(), routeHandlerFunction)
                    .after(afterFunction1)
                    .after(afterFunction2)
                    .after(afterFunction3)
                    .build())
        .onError(Throwable -> true, exceptionHandlerFunction)
        .build();

The order of execution I'm seeing is:

beforeFunction1 -> beforeFunction2 -> beforeFunction3 -> routeHandlerFunction -> afterFunction3 -> afterFunction2 -> afterFunction1

If there is a fixed order to expect I can define my route based on that. Is there documentation about this anywhere?

Thanks,
Gotham

@spencergibb
Copy link
Member

spencergibb commented May 8, 2024

Your finding are correct about the order. Let's use this to document that fact. Before and after functions are special cases of HandlerFilterFunction, which has both before and after. So the more accurate execution is this

beforeFunction1 -> beforeFunction2 -> beforeFunction3 -> afterFunction1 (no op) -> afterFunction2 (no op) -> afterFunction3 (no op) -> 

routeHandlerFunction -> 

afterFunction3 -> afterFunction2 -> afterFunction1 -> beforeFunction3 (no op) -> beforeFunction2 (no op) -> beforeFunction1 (no op) 

All the before parts are executed in the order defined, then all the afters are executed in the reverse order they are defined. The fact that you put the route() call in between the calls to before() and `after() has no bearing on the order.

@gatewaynovice
Copy link
Author

gatewaynovice commented May 8, 2024

Thanks that helps. Would the after() functions still run if there is an exception that is handled by the onError() functions?

@spencergibb
Copy link
Member

I'm not positive. @poutsma could say with certainty.

@spencergibb
Copy link
Member

Looking at the code, onError() is just a specialization of a handler filter function. Looking at the code it looks like they might still run. What is your experience? https://github.com/spring-projects/spring-framework/blob/8137cc95669690f3e4055d6ccf484e98a07b6703/spring-webmvc/src/main/java/org/springframework/web/servlet/function/HandlerFilterFunction.java#L106-L129

@gatewaynovice
Copy link
Author

I'm not seeing the after() functions run after an exception has been handled in my example above, not sure if that is because I defined the onError() handler outside the nested route. The exception happens to be thrown by one of the before() functions in my testing. I had to define the onError() handler outside the nested route otherwise any exceptions being thrown were not getting handled.

@poutsma
Copy link

poutsma commented May 15, 2024

Thanks that helps. Would the after() functions still run if there is an exception that is handled by the onError() functions?

As you already found out, they don't.

I'm not seeing the after() functions run after an exception has been handled in my example above, not sure if that is because I defined the onError() handler outside the nested route. The exception happens to be thrown by one of the before() functions in my testing. I had to define the onError() handler outside the nested route otherwise any exceptions being thrown were not getting handled.

Filters—such as those registered with before, after, and onError—are scoped, and only apply to the routes in the same nesting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants