spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Global filters for MVC Gateway

Open Andross96 opened this issue 2 years ago • 1 comments

Question

How to create a filter which allows us, if the request match any configured route, to perform a specific action on the request (like adding a header) ?

Using the GlobalFilter in webflux, the filter was executed only if the request matched a configured route and we were able to add our custom header easily, no matter the route and/or the path nor http method. According to the documentation and another related issue (#3143), we have now to use Spring WebMvc.fn. But we can not find a way using WebMvc.fn to simply filter on an already existing configured route only.

So far, we have tried:

@Bean
public RouterFunction<ServerResponse> customFilter() {
    return GatewayRouterFunctions.route("**")
            .route(RequestPredicates.all(), http()) // but is triggerred for any endpoint.. + "http()" url is not set..
            .filter((request, next) -> return next.handle(request)) // we are able to put our header logic here, or by using #before, this part is OK.
            .build();
}

We can not only put the "filter" part, because it requires a route (GET, POST...) We have also tried by iterating over the configured GatewayMvcProperties, but still we do not find a way to make this work, nor it seems an "official" or standard way.


For a better code comprehension, we are simply trying to translate our previous webflux GlobalFilter to make it work on MVC gateway:

@Component
public class CustomFilter implements GlobalFilter {
    // Adding a custom header for routed requests (existing in configuration, no matter the route and/or http method)
    @Override
    public Mono<Void> filter(final ServerWebExchange exchange, final GatewayFilterChain chain) {
        // We can even get the configuration of the route: final Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        final ServerHttpRequest request = exchange.getRequest()
                .mutate()
                .header("custom", customHeaderValue) // adding our custom header
                .build();
        return chain.filter(exchange.mutate().request(request).build());
    }
}

Please advice.

Andross96 avatar Jan 25 '24 10:01 Andross96

@Andross96 i hope this helps you https://stackoverflow.com/questions/77744944/spring-cloud-gateway-server-mvc-add-header-from-authentication-for-all-requests . But pay attention https://stackoverflow.com/questions/78009495/spring-cloud-gateway-mvc-unpredictable-order-of-operation-of-custom-filters

NiiazKhan avatar Feb 18 '24 19:02 NiiazKhan

Hello @NiiazKhan, thank you for your answer. Unfortunately, this does not help because we still have to manually create RouterFunctions and translate everything from application properties (including predicates..) in order to create our filter. We are really looking for a reactive GlobalFilter like in MVC.

Andross96 avatar Feb 27 '24 07:02 Andross96

@Andross96 I am monitoring these issues: https://github.com/spring-cloud/spring-cloud-gateway/issues/3250 https://github.com/spring-cloud/spring-cloud-gateway/issues/3268

NiiazKhan avatar Feb 27 '24 08:02 NiiazKhan

I'm not sure mvc gateway requires a separate implementation of filters besides what mvc.fn provides or just a servlet filter

spencergibb avatar Feb 27 '24 15:02 spencergibb

We actually have no "proper" way to create filter on matched gateway routes configured inside application.yml, respecting the configured predicates.

Let's take a simple example:

spring:
  cloud:
    gateway:
      mvc:
        routes:
          - id: backend
            uri: http://localhost:8080
            predicates:
              - Path=/api/**
              # possibily multiple/variable predicates per route

Using the actual mvc.fn or servlet filter, we have to recreate the entire RouterFunction based on the configuration file, so we have to get the properties of the routes (to set the backend url), and the harsh part is to "translate" all predicates entered (like GatewayMvcPropertiesBeanDefinitionRegistrar#getRouterFunction) because we need the predicates to be validated first before executing our filter.

Are we missing something ?

Andross96 avatar Feb 27 '24 16:02 Andross96

So you want filters that only run for gateway matched routes. What about #3177 default filters for mvc gateway?

spencergibb avatar Feb 27 '24 16:02 spencergibb

Yes exactly, but there is a main difference which make the default filter still not as suitable as global filters unfortunately : with default filter, you have to write manually the filters in your application properties (sometimes multiple,complexes..), while global filters are executed by default for every matched routes, without worrying about additional application properties configuration.

It's a bit sad that migrating from reactive gateway to MVC isn't feasible while preserving identical behaviors. Both versions should ideally offer comparable features, ensuring consistency and compatibility across applications stack.

Andross96 avatar Feb 27 '24 17:02 Andross96

It's a bit sad that migrating from reactive gateway to MVC isn't feasible while preserving identical behaviors.

This is never going to be possible to have identical behavior, similar yes, identical never.

So, to create a global filter you have to create a bean, why can't default filters be added with a bean?

spencergibb avatar Feb 27 '24 18:02 spencergibb

That could do the trick ! But I do not see how to add default filter with a bean ? The only documentation on default filter is related to writing properties, not beans ?

Andross96 avatar Feb 27 '24 20:02 Andross96

gateway mvc doesn't support default filters at all yet, so both config and bean configuration would be supported. That documentation if for the reactive gateway.

spencergibb avatar Feb 27 '24 20:02 spencergibb

Closing in favor of #3177

spencergibb avatar Feb 27 '24 20:02 spencergibb