Master Spring Cloud Gateway: Routing, Custom Predicates & Filters Explained
This guide walks through using Spring Cloud Gateway to replace direct service calls with a unified entry point, covering service discovery, load‑balanced routing, predicate definitions, custom predicates, rewrite‑path filters, global filters, and CORS configuration for microservice architectures.
Gateway Overview
In simple scenarios the frontend calls a single order service (e.g., port 9000), but real‑world systems have multiple order services, so the frontend must decide which one to invoke.
A Gateway acts as a unified backend entry point. It uses Nacos for service discovery, performs load‑balanced routing, and can handle traffic control, authentication, protocol conversion, monitoring, and security.
Spring Cloud Gateway
Spring Cloud provides two gateway frameworks:
Server : core infrastructure for inbound requests (routing, filtering, forwarding) suitable for standardized routing configurations.
Proxy Exchange : auxiliary tool for outbound requests, suitable for custom logic such as data aggregation or conditional routing.
Both WebFlux (non‑blocking, Netty) and MVC (blocking) implementations are available. The tutorial uses the WebFlux version via spring-cloud-starter-gateway.
Business Implementation
Requirements:
All /api/order/** requests should be routed to the service-order microservice.
All /api/product/** requests should be routed to the service-product microservice.
Both routes must use load‑balancing.
The following diagram illustrates the model:
Basic Setup
Create a gateway module under the spring-cloud-demo project and add the required dependencies:
<dependencies>
<!-- Nacos service discovery for the gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Spring Cloud Gateway starter (WebFlux) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Load‑balancer support -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>Add the main application class:
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}Configure the gateway in application.yml:
spring:
application: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
profiles:
include: route
server:
port: 80Define routing rules in application‑route.yml:
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- Path=/api/order/**
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**Key fields of a RouteDefinition: id: unique identifier of the route. predicates: list of match conditions. filters: list of filters applied to the request. uri: destination address (e.g., lb://service‑order). metadata: custom key‑value pairs. order: priority (smaller value = higher priority).
Note: the predicate list is an array; all conditions must be satisfied for the route to match.
Predicate Definitions
A predicate is represented by PredicateDefinition with a name and arguments. The common Path predicate can be written in a short form ( Path=/api/order/**) which the framework expands to a configuration object containing patterns and matchTrailingSlash flags.
Example of the expanded form:
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- name: Path
args:
patterns: /api/order/**
matchTrailingSlash: trueCustom Predicate Example
Sometimes built‑in predicates are insufficient. The following custom predicate checks that a query parameter user equals zhangsan (a “grey” user):
@Component
public class GreyRoutePredicateFactory extends AbstractRoutePredicateFactory<GreyRoutePredicateFactory.Config> {
public GreyRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return List.of("param", "value");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
ServerHttpRequest request = exchange.getRequest();
String first = request.getQueryParams().getFirst(config.getParam());
return StringUtils.hasText(first) && first.equals(config.getValue());
};
}
@Validated
public static class Config {
@NotEmpty private String param;
@NotEmpty private String value;
public String getParam() { return param; }
public void setParam(String param) { this.param = param; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
}Use it in the routing file:
spring:
cloud:
gateway:
routes:
- id: baidu-route
uri: https://www.baidu.com
predicates:
- name: Path
args:
pattern: /s
- name: Query
args:
param: wd
regexp: springboot
- name: Grey
args:
param: user
regexp: zhangsanFilters
Filters can modify requests and responses. The most common is RewritePath, which uses a regular expression to rewrite the request URI.
Example: rewrite /api/order/xxx to /xxx so that downstream services do not need the /api/order prefix.
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order
predicates:
- Path=/api/order/**
filters:
- RewritePath=/api/order/(?<segment>.*),/${segment}
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/product/(?<segment>.*),/${segment}Default filters apply to all routes. Adding a response header globally:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Source,apiGlobal filters are custom Java classes that implement GlobalFilter. The example below logs request duration:
@Component
@Slf4j
public class RtGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String uri = exchange.getRequest().getURI().toString();
long start = System.currentTimeMillis();
log.info("Request start, uri:{}, start:{}", uri, start);
return chain.filter(exchange).doFinally(signal -> {
long end = System.currentTimeMillis();
log.info("Request end, uri:{}, cost:{}", uri, end - start);
});
}
@Override
public int getOrder() { return 0; }
}Custom filter example that adds a one‑time token to the response header:
@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Resource
private JwtUtil jwtUtil;
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
String value = config.getValue();
if ("uuid".equalsIgnoreCase(value)) {
value = UUID.randomUUID().toString();
}
if ("jwt".equalsIgnoreCase(value)) {
value = jwtUtil.generateToken("admin");
}
headers.add(config.getName(), value);
}));
}
}CORS Configuration
To allow all cross‑origin requests, add a global CORS configuration:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origin-patterns: '*'
allowed-headers: '*'
allowed-methods: '*'After this configuration the response headers contain the necessary Access-Control-* entries, enabling browsers to perform both simple and pre‑flight requests without restriction.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
