Configuración de proyecto con Spring Gateway
Spring Cloud Gateway ha evolucionado considerablemente desde sus primeras versiones. Inicialmente, esta tecnología se basaba exclusivamente en el stack reactivo de Spring WebFlux, lo que requería familiarizarse con programación reactiva para implementar un gateway. Sin embargo, las versiones modernas ofrecen una alternativa basada en el stack servlet tradicional, facilitando su adopción para desarrolladores que ya conocen Spring MVC.
Stack reactivo vs stack servlet
Stack reactivo, usando webflux:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Stack MVC basado en servlet, el tradicional, sin usar programación reactiva:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Configuración inicial del proyecto
Para crear un proyecto Spring Cloud Gateway con el stack servlet, comenzamos con un proyecto Spring Boot estándar. La estructura del pom.xml
debe incluir las dependencias fundamentales:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Configuración básica del Gateway
La configuración del gateway se realiza principalmente a través del archivo application.yml
.
En la propia aplicación gateway lo que hacemos es conectar al config server que será el que nos proporcione la configuración centralizada:
spring:
cloud:
config:
uri: http://localhost:8888
application:
name: gateway
Y por tanto en el repositorio git donde están todos los archivos de configuración habrá que crear uno para el gateway:
Esta configuración define el comportamiento básico del gateway sin necesidad de código Java adicional, con ejemplo de cómo configurar aplicaciones microservicios para enrutarlas:
server:
port: 8080
eureka:
client:
serviceUrl:
defaultZone: "http://${app.eureka-server}:8761/eureka/"
initialInstanceInfoReplicationIntervalSeconds: 5
registryFetchIntervalSeconds: 5
instance:
leaseRenewalIntervalInSeconds: 5
leaseExpirationDurationInSeconds: 5
spring:
cloud:
gateway:
routes:
# MICROSERVICIO customers
- id: customers
uri: lb://customers
predicates:
- Path=/customers/**
# MICROSERVICIO directions
- id: directions
uri: lb://directions
predicates:
- Path=/directions/**
# Eureka
- id: eureka-api
uri: http://localhost:8761
predicates:
- Path=/eureka/api/{segment}
filters:
- SetPath=/eureka/{segment}
- id: eureka-web-start
uri: http://localhost:8761
predicates:
- Path=/eureka/web
filters:
- SetPath=/
- id: eureka-web-other
uri: http://localhost:8761
predicates:
- Path=/eureka/**
Clase principal de la aplicación
La clase principal del gateway es una aplicación Spring Boot estándar. No requiere anotaciones especiales para el gateway MVC, aunque sí necesitamos habilitar el cliente Eureka:
package com.ejemplo.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
Verificación de la configuración
Una vez configurado el proyecto, podemos verificar que el gateway arranca correctamente. Al ejecutar la aplicación, deberíamos ver en los logs:
INFO --- [ main] c.ejemplo.gateway.ApiGatewayApplication : Started ApiGatewayApplication in 3.245 seconds
INFO --- [ main] o.s.c.g.m.GatewayMvcConfiguration : Gateway MVC enabled
El gateway estará disponible en http://localhost:8080
y se registrará automáticamente en Eureka Server si está ejecutándose en http://localhost:8761
.
Diferencias clave del stack MVC
Al utilizar el stack servlet, obtenemos varias ventajas importantes:
- Compatibilidad total con Spring MVC y sus anotaciones familiares
- Debugging más sencillo al usar el modelo de threading tradicional
- Integración directa con herramientas existentes de Spring Boot
- Curva de aprendizaje reducida para desarrolladores que conocen Spring MVC
La principal diferencia técnica es que el gateway MVC procesa las peticiones de forma síncrona utilizando el modelo de threading tradicional de Tomcat, mientras que la versión reactiva utiliza un modelo asíncrono basado en Netty.
Esta configuración inicial nos proporciona la base sólida necesaria para implementar rutas, predicados y filtros en nuestro gateway, aprovechando toda la potencia de Spring Cloud Gateway sin la complejidad adicional de la programación reactiva.
No obstante, en la actualidad es habitual utilizar webflux con todo el stack reactivo ya que ofrece un sistema no bloqueante ideal para escalar aplicaciones de alto rendimiento.
Rutas y predicados (Path, Method)
Las rutas en Spring Cloud Gateway definen cómo se dirigen las peticiones entrantes hacia los servicios backend correspondientes. Cada ruta combina uno o varios predicados que determinan si una petición coincide con esa ruta específica, junto con filtros que pueden modificar la petición o respuesta.
Conceptos fundamentales
Un predicado actúa como una condición que debe cumplirse para que una petición sea procesada por una ruta determinada. Spring Cloud Gateway MVC ofrece múltiples tipos de predicados, siendo los más utilizados Path y Method, que permiten enrutar basándose en la URL y el método HTTP respectivamente.
La definición de rutas se realiza principalmente mediante configuración declarativa en el archivo application.yml
, aunque también es posible definirlas programáticamente mediante código Java.
Configuración de rutas con predicado Path
El predicado Path permite enrutar peticiones basándose en el patrón de la URL. Este predicado utiliza la sintaxis de Spring PathMatcher, que incluye soporte para comodines y variables de ruta.
spring:
cloud:
gateway:
mvc:
routes:
- id: usuarios-service
uri: http://localhost:8081
predicates:
- Path=/api/usuarios/**
- id: productos-service
uri: http://localhost:8082
predicates:
- Path=/api/productos/**
- id: pedidos-service
uri: http://localhost:8083
predicates:
- Path=/api/pedidos/**
En este ejemplo, cualquier petición que comience con /api/usuarios/
será enrutada hacia el servicio ejecutándose en el puerto 8081. El patrón /**
indica que coincide con cualquier sub-ruta después del prefijo especificado.
Patrones avanzados con Path
El predicado Path soporta patrones más específicos utilizando variables y expresiones regulares:
spring:
cloud:
gateway:
mvc:
routes:
- id: usuario-por-id
uri: http://localhost:8081
predicates:
- Path=/api/usuarios/{id}
- id: productos-por-categoria
uri: http://localhost:8082
predicates:
- Path=/api/productos/categoria/{categoria}/**
- id: busqueda-flexible
uri: http://localhost:8083
predicates:
- Path=/api/search/{tipo:[a-z]+}
Las variables de ruta como {id}
y {categoria}
se capturan automáticamente y pueden ser utilizadas por filtros posteriores. La expresión {tipo:[a-z]+}
utiliza una expresión regular para validar que el parámetro tipo contenga únicamente letras minúsculas.
Configuración de rutas con predicado Method
El predicado Method filtra peticiones basándose en el método HTTP utilizado. Es especialmente útil cuando queremos que diferentes métodos HTTP sean manejados por servicios distintos o requieran tratamientos diferenciados.
spring:
cloud:
gateway:
mvc:
routes:
- id: usuarios-lectura
uri: http://localhost:8081
predicates:
- Path=/api/usuarios/**
- Method=GET
- id: usuarios-escritura
uri: http://localhost:8082
predicates:
- Path=/api/usuarios/**
- Method=POST,PUT,DELETE
En este caso, las peticiones GET a /api/usuarios/**
se dirigen al servicio de solo lectura en el puerto 8081, mientras que las operaciones de escritura (POST, PUT, DELETE) se enrutan al servicio especializado en escrituras en el puerto 8082.
Combinación de múltiples predicados
Los predicados pueden combinarse para crear condiciones más específicas. Cuando se especifican múltiples predicados para una ruta, todos deben cumplirse para que la petición coincida:
spring:
cloud:
gateway:
mvc:
routes:
- id: api-admin
uri: http://localhost:8084
predicates:
- Path=/api/admin/**
- Method=POST,PUT,DELETE
- id: api-publica
uri: http://localhost:8085
predicates:
- Path=/api/public/**
- Method=GET
- id: reportes-especificos
uri: http://localhost:8086
predicates:
- Path=/api/reportes/{tipo}
- Method=GET
Integración con Eureka Service Discovery
Una de las ventajas principales del gateway es su integración con Eureka para service discovery. En lugar de especificar URIs estáticas, podemos referenciar servicios por su nombre registrado en Eureka:
spring:
cloud:
gateway:
mvc:
routes:
- id: usuarios-service
uri: lb://USUARIOS-SERVICE
predicates:
- Path=/api/usuarios/**
- id: productos-service
uri: lb://PRODUCTOS-SERVICE
predicates:
- Path=/api/productos/**
- Method=GET,POST
- id: inventario-service
uri: lb://INVENTARIO-SERVICE
predicates:
- Path=/api/inventario/**
- Method=GET,PUT
El prefijo lb:// indica a Spring Cloud Gateway que debe utilizar el LoadBalancer para resolver el nombre del servicio. USUARIOS-SERVICE
debe coincidir con el nombre registrado en Eureka Server.
Configuración programática de rutas
Además de la configuración YAML, es posible definir rutas programáticamente mediante beans de configuración:
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("usuarios-api", r -> r
.path("/api/usuarios/**")
.and()
.method(HttpMethod.GET, HttpMethod.POST)
.uri("lb://USUARIOS-SERVICE")
)
.route("productos-especificos", r -> r
.path("/api/productos/{categoria}")
.and()
.method(HttpMethod.GET)
.uri("lb://PRODUCTOS-SERVICE")
)
.build();
}
}
Esta aproximación programática ofrece mayor flexibilidad para lógica compleja de enrutado, aunque para la mayoría de casos la configuración declarativa en YAML resulta más clara y mantenible.
Precedencia y orden de evaluación
Cuando múltiples rutas pueden coincidir con una petición, Spring Cloud Gateway evalúa las rutas en el orden definido en la configuración. La primera ruta que coincida será la utilizada:
spring:
cloud:
gateway:
mvc:
routes:
# Ruta más específica - se evalúa primero
- id: usuarios-admin
uri: lb://USUARIOS-ADMIN-SERVICE
predicates:
- Path=/api/usuarios/admin/**
- Method=POST,PUT,DELETE
# Ruta más general - se evalúa después
- id: usuarios-general
uri: lb://USUARIOS-SERVICE
predicates:
- Path=/api/usuarios/**
Es fundamental colocar las rutas más específicas antes que las generales para evitar que peticiones sean capturadas por rutas demasiado amplias.
Debugging y monitorización de rutas
Para facilitar el desarrollo y depuración, podemos habilitar logging detallado que nos muestre qué rutas se están evaluando:
logging:
level:
org.springframework.cloud.gateway.mvc: DEBUG
org.springframework.web.servlet.mvc.method.annotation: DEBUG
Con este logging activado, podremos observar en los logs cómo cada petición es evaluada contra los predicados definidos, facilitando la identificación de problemas de configuración o comportamientos inesperados en el enrutado.
Filtros básicos y reescritura de paths
Los filtros en Spring Cloud Gateway permiten modificar las peticiones entrantes y las respuestas salientes de forma transparente. Actúan como middleware que se ejecuta antes o después del enrutado hacia los servicios backend, proporcionando funcionalidades como transformación de URLs, adición de headers, autenticación y logging.
Tipos de filtros
Spring Cloud Gateway MVC ofrece dos categorías principales de filtros:
- Filtros globales: Se aplican a todas las rutas configuradas en el gateway
- Filtros específicos de ruta: Se aplican únicamente a rutas individuales
Los filtros se ejecutan en un orden determinado, permitiendo crear cadenas de procesamiento que transforman gradualmente las peticiones y respuestas.
Filtros globales básicos
Los filtros globales se configuran en la sección default-filters
y afectan a todas las rutas del gateway:
spring:
cloud:
gateway:
mvc:
default-filters:
- AddRequestHeader=X-Gateway-Source, api-gateway
- AddResponseHeader=X-Powered-By, Spring-Cloud-Gateway
- RemoveRequestHeader=X-Internal-Auth
- RemoveResponseHeader=Server
Este ejemplo añade headers informativos tanto a peticiones como respuestas, y elimina headers que no queremos exponer externamente.
Reescritura de paths con RewritePath
El filtro RewritePath es uno de los más utilizados, ya que permite modificar la URL antes de enviarla al servicio backend. Utiliza expresiones regulares para capturar partes de la URL original y recomponerla:
spring:
cloud:
gateway:
mvc:
routes:
- id: usuarios-v1
uri: lb://USUARIOS-SERVICE
predicates:
- Path=/v1/usuarios/**
filters:
- RewritePath=/v1/usuarios/(?<segment>.*), /api/users/$\{segment}
- id: productos-legacy
uri: lb://PRODUCTOS-SERVICE
predicates:
- Path=/legacy/productos/**
filters:
- RewritePath=/legacy/productos/(?<remaining>.*), /api/v2/products/$\{remaining}
En el primer ejemplo, una petición a /v1/usuarios/123/perfil
se transforma en /api/users/123/perfil
antes de llegar al servicio backend. El grupo de captura (?<segment>.*)
captura todo después de /v1/usuarios/
y lo utiliza en la URL de destino.
Filtros de prefijo y eliminación de path
Para casos más simples de transformación de URLs, disponemos de los filtros StripPrefix y PrefixPath:
spring:
cloud:
gateway:
mvc:
routes:
- id: api-interna
uri: lb://SERVICIO-INTERNO
predicates:
- Path=/external/api/**
filters:
- StripPrefix=2 # Elimina /external/api
- id: servicio-legacy
uri: lb://SERVICIO-LEGACY
predicates:
- Path=/new/**
filters:
- StripPrefix=1 # Elimina /new
- PrefixPath=/legacy/v1 # Añade prefijo
Con StripPrefix=2, una petición a /external/api/usuarios/123
se convierte en /usuarios/123
. El valor numérico indica cuántos segmentos de path eliminar desde el inicio.
Filtros de manipulación de headers
Los filtros de headers permiten añadir, modificar o eliminar headers tanto en peticiones como en respuestas:
spring:
cloud:
gateway:
mvc:
routes:
- id: servicio-autenticado
uri: lb://SERVICIO-SEGURO
predicates:
- Path=/secure/**
filters:
- AddRequestHeader=X-Gateway-Auth, validated
- AddRequestHeader=X-Request-ID, #{T(java.util.UUID).randomUUID().toString()}
- RemoveResponseHeader=X-Internal-Token
- SetResponseHeader=Cache-Control, no-cache
El filtro utiliza Spring Expression Language (SpEL) para generar valores dinámicos, como el UUID único para cada petición en el header X-Request-ID
.
Filtros de parámetros de consulta
Es posible manipular los query parameters de las URLs utilizando filtros específicos:
spring:
cloud:
gateway:
mvc:
routes:
- id: servicio-con-parametros
uri: lb://SERVICIO-BACKEND
predicates:
- Path=/api/search/**
filters:
- AddRequestParameter=source, gateway
- RemoveRequestParameter=debug
- RewritePath=/api/search, /search
Estos filtros añaden parámetros automáticamente (source=gateway
), eliminan otros que no queremos pasar al backend (debug
) y reescriben la ruta simultáneamente.
Combinación de múltiples filtros
Los filtros se pueden combinar para crear transformaciones complejas. Se ejecutan en el orden especificado en la configuración:
spring:
cloud:
gateway:
mvc:
routes:
- id: transformacion-completa
uri: lb://NUEVO-SERVICIO
predicates:
- Path=/old-api/v1/**
filters:
- StripPrefix=2 # /old-api/v1/users -> /users
- PrefixPath=/api/v2 # /users -> /api/v2/users
- AddRequestHeader=X-Version, v2 # Añadir header de versión
- AddRequestHeader=X-Migrated, true
- RemoveRequestParameter=legacy # Limpiar parámetros legacy
Esta configuración migra peticiones de una API legacy hacia una nueva versión, transformando completamente la URL y añadiendo metadata sobre la migración.
Filtros condicionales con SetPath
El filtro SetPath permite definir rutas completamente nuevas basándose en variables capturadas por predicados:
spring:
cloud:
gateway:
mvc:
routes:
- id: redirection-dinamica
uri: lb://SERVICIO-FLEXIBLE
predicates:
- Path=/redirect/{service}/{version}/**
filters:
- SetPath=/{service}/api/{version}/{remaining}
- AddRequestHeader=X-Original-Path, /redirect/{service}/{version}/{remaining}
Las variables {service}
, {version}
y {remaining}
capturadas por el predicado Path se reutilizan en el filtro SetPath para construir la nueva ruta dinámicamente.
Configuración programática de filtros
Para casos avanzados, es posible definir filtros programáticamente:
@Configuration
public class GatewayFilterConfig {
@Bean
public RouteLocator filtrosPersonalizados(RouteLocatorBuilder builder) {
return builder.routes()
.route("usuarios-transformados", r -> r
.path("/legacy/users/**")
.filters(f -> f
.rewritePath("/legacy/users/(?<segment>.*)", "/api/v2/usuarios/${segment}")
.addRequestHeader("X-Transformation", "legacy-to-v2")
.addRequestHeader("X-Timestamp",
String.valueOf(System.currentTimeMillis()))
.removeResponseHeader("X-Internal-Version")
)
.uri("lb://USUARIOS-SERVICE")
)
.build();
}
}
Debugging y monitorización de filtros
Para depurar el comportamiento de los filtros, podemos activar logging detallado:
logging:
level:
org.springframework.cloud.gateway.mvc.filter: DEBUG
org.springframework.cloud.gateway.mvc.handler: DEBUG
También es útil añadir filtros de logging que nos muestren las transformaciones aplicadas:
spring:
cloud:
gateway:
mvc:
routes:
- id: servicio-debug
uri: lb://MI-SERVICIO
predicates:
- Path=/debug/**
filters:
- RewritePath=/debug/(?<path>.*), /api/${path}
- AddRequestHeader=X-Debug-Original, /debug/${path}
- AddRequestHeader=X-Debug-Rewritten, /api/${path}
Filtros de respuesta y manejo de errores
Los filtros también pueden modificar las respuestas que regresan desde los servicios backend:
spring:
cloud:
gateway:
mvc:
default-filters:
- AddResponseHeader=X-Response-Time, #{T(System).currentTimeMillis()}
- RemoveResponseHeader=X-Internal-Service
routes:
- id: servicio-con-respuesta-personalizada
uri: lb://MI-SERVICIO
predicates:
- Path=/api/**
filters:
- AddResponseHeader=X-Gateway-Version, 1.0
- SetResponseHeader=Access-Control-Allow-Origin, "*"
Esta configuración enriquece las respuestas con información adicional y configura headers CORS automáticamente para todas las rutas que coincidan con el patrón /api/**
.
Los filtros proporcionan una flexibilidad extraordinaria para adaptar el comportamiento del gateway a las necesidades específicas de cada arquitectura de microservicios, permitiendo transformaciones complejas sin requerir cambios en los servicios backend.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en SpringBoot
Documentación oficial de SpringBoot
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SpringBoot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de SpringBoot
Explora más contenido relacionado con SpringBoot y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender la configuración inicial de un proyecto Spring Cloud Gateway usando el stack servlet.
- Diferenciar entre stack reactivo y stack servlet en Spring Cloud Gateway.
- Configurar rutas y predicados Path y Method para enrutar peticiones.
- Aplicar filtros básicos y avanzados para modificar peticiones y respuestas.
- Integrar el gateway con Eureka para service discovery y balanceo de carga.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje