Configurar pre Filtros y post Filtros en Spring Cloud Gateway

En el mundo de las arquitecturas de microservicios, Spring Cloud Gateway actúa como el primer punto de entrada a nuestro sistema. Este componente no solo se encarga del enrutamiento de peticiones, sino que también permite interceptarlas, modificarlas o monitorizarlas antes o después de que lleguen a su destino. ¿Cómo lo consigue? Mediante filtros.

 

 

En este post vamos a tratar de profundizar en la configuración de filtros en Spring Cloud Gateway, abordando sus tipos, cómo declararlos en distintas formas y cuándo conviene usar una u otra estrategia.

 

¿Qué es Spring Cloud Gateway?

 

Spring Cloud Gateway es el API Gateway oficial del ecosistema Spring. Proporciona una forma sencilla y potente de enrutar solicitudes a servicios backend, además de aplicar funcionalidades transversales como autenticación, logging, modificación de cabeceras, balanceo de carga, entre otros.

 

Su arquitectura se basa en un sistema de filtros altamente configurable, lo cual permite a los desarrolladores personalizar el comportamiento de las peticiones que atraviesan el gateway.

 

 

🚧 Tipos de filtros en Spring Cloud Gateway

 

Spring Cloud Gateway permite utilizar dos tipos principales de filtros:

  • Pre-filters: Se ejecutan antes de que la petición llegue al microservicio destino.

  • Post-filters: Se ejecutan después de recibir la respuesta del microservicio destino.

Ambos pueden usarse para acciones como validar tokens, registrar logs, modificar rutas o respuestas, añadir cabeceras, etc.

 

 

Cómo funcionan internamente los filtros

 

El flujo general en una petición que atraviesa Spring Cloud Gateway es el siguiente:


Cliente → Gateway → Pre-Filters → Microservicio → Post-Filters → Respuesta

Esto significa que cualquier lógica en un pre-filtro se ejecutará antes del microservicio, y los post-filtros se aplicarán a la respuesta que se retorna al cliente.

 

 

✅ Configuración de filtros en una clase única

 

Una de las formas más directas de definir filtros en Spring Gateway es mediante una clase de configuración. Vamos a ver un ejemplo paso a paso.

 

Paso 1: Crear una clase con @Configuration

 
Hay que tener en cuenta los siguientes detalles importantes:
  • La clase debe ser marcada con el tag @Configuration

  • El método que contiene el filtro está implementado como un bean con el tag @Bean

  • Para definir el orden de ejecución entre varios filtros hay que usar el tag @Order

  • La lógica pre-filtro del método debe ser incluida dentro de una función Lambda.

  • La lógica post-filtro del método debe ser incluida en un Runnable anexado a la cadena de filtros mediante la función then().

     
 👉 Clase "GlobalFiltersConfiguration":
 
@Configuration
public class GlobalFiltersConfiguration {
   
    final Logger logger = LoggerFactory.getLogger(GlobalFiltersConfiguration.class);

    @Order(1)
    @Bean
    public GlobalFilter preFilter() {
 
        GlobalFilter filtro = (ServerWebExchange exchange, GatewayFilterChain chain) -> {
            logger.info("Ejecutando pre-filtro personalizado ... ");
           
            // Creamos la configuracion del post Filtro
            Runnable runPostFiltro = () -> {
                logger.info("Ejecutando post-filtro despues del microservicio...");
            };
            Mono<Void> monoPostFiltro = Mono.fromRunnable(runPostFiltro);
           
            // Delegamos la ejecucion al siguiente filtro de la cadena y ejecutamos el postFiltro
            Mono<Void> ret = chain.filter(exchange).then(monoPostFiltro);
            return ret;
        };
       
        return filtro;

    }
}
 

 

warning Atención: El orden de ejecución de @Order será distinto en el caso de Pre-Filter y de Post-Filter

  • PreFiltros: se ejecutan primero los de orden inferior 0-1-2-3-4

  • PostFiltros: se ejecutan primero los de orden superior 4-3-2-1-0

 

🔄 Configuración de filtros en clases separadas

 

Cuando se desea mantener una arquitectura más modular, es recomendable separar los filtros en clases individuales. Para ello:

 

Paso 1: Pre-filter en clase propia

 
Para construir el filtro Pre-Filter hay que tener en cuenta lo siguiente: 
  • La clase se marca con el tag @Component 
  • La clase debe heredar de las interfaces GlobalFilter y Ordered.
  • Se debe implementar el médodo filter() con la lógica de filtrado Pre-Filter. Este método debe devolver un Mono<Void> que delegue la ejecución al siguiente filtro de la cadena.
  • Para establecer el orden de ejecución del filtro, hay que implemenentar el método getOrder() 
 
👉 Clase "MyPreFilter":
 
 
/**
 * Global PreFilter
 */
@Component
public class MyPreFilter implements GlobalFilter, Ordered {

    final Logger logger = LoggerFactory.getLogger(MyPreFilter.class);
   
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // TODO Auto-generated method stub
       
        logger.info("Mi primer Pre-filter se ha ejecutado...");
       
        // Obtenemos el path de la peticion entrante
        String requestPath = exchange.getRequest().getPath().toString();
        logger.info("Request path = " + requestPath);
       
        // Obtenemos los headers de la peticion
        HttpHeaders headers = exchange.getRequest().getHeaders();
        Set<String> headerNames = headers.keySet();
       
        logger.info("Http Headers ...");
        // Obtenemos la lista de key-value de los headers de la peticion
        Consumer <? super String> consum = (String headerName) -> {
            String headerValue = headers.getFirst(headerName);
            logger.info(headerName + ": " + headerValue);
        };
       
        headerNames.forEach(consum);
       
       
        // Delegar al nuevo filtro de la cadena
        Mono<Void> ret = chain.filter(exchange);
       
        return ret;
    }

    /**
     * Orden de ejecucion del pre-filtro: 0-1-2-3-4
     * Se ejecuta el primero: 0
     */
    @Override
    public int getOrder() {
        // TODO Auto-generated method stub
        return 0;
    }

}
 

 

Paso 2: Post-filter en clase propia

 
Si queremos un Post-Filter, entonces hay que crear un Runnable con la lógica que se ejecutará con posterioridad a la delegación de la cadena.  
 
👉 Clase "MyPostFilter": 
 
/**
 * Global Post Filter
 */
@Component
public class MyPostFilter implements GlobalFilter, Ordered {

    final Logger logger = LoggerFactory.getLogger(MyPreFilter.class);
   
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // TODO Auto-generated method stub
       
        logger.info("clase MyPostFilter");
       
        // Crear Runnable
        Runnable run1 = () -> {
            // TODO
            logger.info("Mi ultimo post-filter se ha ejecutado...");
        };
       
        //
        Mono<Void> mon1 = Mono.fromRunnable(run1);
       
        // Delegar al nuevo filtro de la cadena
        // Despues se ejecuta el Mono que contiene el runnable
        Mono<Void> ret = chain.filter(exchange).then(mon1);
       
        return ret;
    }

    /**
     * Orden de ejecucion del Post Filtro: 4-3-2-1-0
     * Se ejecuta el ultimo: 0
     */
    @Override
    public int getOrder() {
        // TODO Auto-generated method stub
        return 0;
    }

}
 
 

⛔ Comparativa entre las dos estrategias

 
A continuación te indicamos cuáles serían algunas de las ventajas y desventajas de utilizar la estrategia de clase única o la estrategia de clases separadas.
 
 
EstrategiaVentajasDesventajas
Clase únicaSimplicidad, menos código repetidoMenor modularidad
Clases separadas       Alta separación de responsabilidades, mantenimiento más sencilloMás clases, posible sobrecarga inicial

 

Ambas opciones son perfectamente válidas. En realidad, la elección de una u otra solución es subjetiva y dependerá del tamaño del proyecto y las necesidades de modularización.

 

 

teacup without handle Buenas prácticas

  • Asigna un orden claro: Usa @Order o implementa getOrder() para definir el flujo exacto.

  • Evita lógica compleja en los filtros: Los filtros deben ser ligeros y enfocados a tareas de infraestructura, no de negocio.

  • Centraliza la trazabilidad: Es común usar filtros para logging de auditoría o monitorización.


🔎 Ejemplo completo en clase única

 
Aquí te dejamos otro ejemplo adicional de cómo sería la implementación de filtros en una clase única de configuración. Esperamos que te sirva para aclarar un poco más los conceptos. 
 
@Configuration
public class GatewayFilters {

    @Order(1)
    @Bean
    public GlobalFilter prePostFilter() {
        return (exchange, chain) -> {
            // Pre
            String path = exchange.getRequest().getPath().toString();
            System.out.println("Pre-filter: Path solicitado: " + path);

            // Post
            Runnable postLogic = () -> System.out.println("Post-filter: Respuesta enviada al cliente");

            return chain.filter(exchange).then(Mono.fromRunnable(postLogic));
        };
    }
}
 
 
 
 

Conclusión

 

Los filtros son uno de los pilares de la arquitectura de Spring Cloud Gateway. Su correcta configuración y uso permiten construir gateways potentes, extensibles y seguros.

 

Puedes comenzar definiendo filtros simples en una clase común y, a medida que crezca tu aplicación, migrar a filtros distribuidos en clases específicas.

 

Además, si te interesa avanzar más, considera combinar filtros con seguridad basada en roles, autenticación JWT o integración con servicios de monitorización como Zipkin o Prometheus.

 

¡Nos vemos en el siguiente post!

Saludos.

 

Comentarios

Entradas populares de este blog

Componentes y Ventanas de Java Swing

Creación de Webservice SOAP mediante Anotaciones

Configurar Apache Tomcat en Eclipse