Spring Cloud Gateway: Core Concepts, Configuration, Filters, Nacos Integration, and Dynamic Routing
This article explains why a gateway is essential in micro‑service architectures, introduces Spring Cloud Gateway’s core concepts such as routes, predicates and filters, shows step‑by‑step setup with Maven dependencies, demonstrates custom local and global filters, and details integration with Nacos for service discovery, load‑balancing, dynamic routing and global error handling.
Hi, I am Chen (the author). To improve article efficiency, I split the "Spring Cloud Advanced" series into smaller parts, each covering one or two key topics.
Previous articles in the series include:
55 pictures reveal the power of Nacos, the soul of micro‑services
OpenFeign Q&A: nine tough questions
Nacos vs Apollo vs Config: 10 dimensions for selection
Five micro‑service registries compared
Sentinel: 17 questions about Alibaba's rate‑limiting tool
Seven distributed‑transaction solutions, why Seata wins (principles + practice)
Why a Gateway Is Needed?
In monolithic architecture a single service is exposed to clients. In micro‑service architecture the system is split into many services, and without a gateway the client would have to know and manage the address of each service, leading to complex client code, duplicated authentication, and cross‑origin issues.
Basic Functions of a Gateway
A gateway is the entry point for all micro‑services. Besides routing, it typically provides authentication, authorization, circuit‑breaking, rate‑limiting, logging, monitoring, etc.
Why Choose Spring Cloud Gateway?
Spring Cloud 1.x used Zuul, but Zuul’s upgrade stalled. Spring Cloud created its own gateway – Spring Cloud Gateway – which inherits Zuul’s ideas while offering better performance and tighter integration with Spring Boot 2.x, WebFlux and Project Reactor.
Key Terminology in Spring Cloud Gateway
Route : The basic building block, composed of an ID, target URI, a set of predicates and a set of filters.
Predicate : Inspired by Java 8’s Predicate, it matches a request based on path, headers, parameters, etc.
Filter : Can modify the request or response before or after routing.
How to Set Up the Gateway
Create a project named cloud-gateway9023 and add the following Maven dependency (remove spring-boot-starter-web to avoid startup errors):
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>What Is a Predicate?
Predicates are Java 8 functional interfaces that return a boolean. Spring Cloud Gateway ships with many built‑in predicates located in org.springframework.cloud.gateway.handler.predicate. Examples include Path, Method, Header, etc.
Example of a weight predicate configuration:
spring:
cloud:
gateway:
routes:
- id: gateway-provider_1
uri: http://localhost:9024
predicates:
- Path=/gateway/provider/**
- Weight=group1,8
- id: gateway-provider_2
uri: http://localhost:9025
predicates:
- Path=/gateway/provider/**
- Weight=group1,2Key fields: id: unique identifier of the route. uri: target address. predicates: list of matching rules.
What Is a Filter?
Filters are similar to those in Spring MVC. They have two lifecycles:
PRE : executed before routing (e.g., authentication, logging).
POST : executed after routing (e.g., adding response headers).
Two scopes:
GatewayFilter : applied to a specific route or a group of routes.
GlobalFilter : applied to all routes automatically.
Local Filter Example – AddResponseHeader
spring:
cloud:
gateway:
routes:
- id: gateway-provider_1
uri: http://localhost:9024
predicates:
- Path=/gateway/provider/**
filters:
- AddResponseHeader=X-Response-Foo,BarAfter a request, the response contains the header X-Response-Foo=Bar.
Custom Local Filter – AuthorizeGatewayFilterFactory
/**
* Class name must end with xxxGatewayFilterFactory
* Simulate authorization verification; actual logic should be customized.
*/
@Component
@Slf4j
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {
private static final String AUTHORIZE_TOKEN = "token";
public AuthorizeGatewayFilterFactory() {
super(AuthorizeGatewayFilterFactory.Config.class);
log.info("Loaded GatewayFilterFactory [Authorize]");
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(AUTHORIZE_TOKEN);
if (token == null) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
ServerHttpResponse response = exchange.getResponse();
if (StringUtils.isEmpty(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
};
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Config {
private boolean enabled;
}
}Configure the filter in a route:
spring:
cloud:
gateway:
routes:
- id: gateway-provider_1
uri: http://localhost:9024
predicates:
- Path=/gateway/provider/**
filters:
- AddResponseHeader=X-Response-Foo,Bar
- Authorize=trueRequests without the token parameter receive HTTP 401; requests with a valid token succeed.
Global Filter Example – AccessLogGlobalFilter
@Slf4j
@Component
@Order(Integer.MIN_VALUE)
public class AccessLogGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
InetSocketAddress remoteAddress = request.getRemoteAddress();
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpStatus status = response.getStatusCode();
log.info("Request path:{}, remote IP:{}, status:{}", path, remoteAddress, status);
}));
}
}The filter logs request path, client IP and response status after each request.
Integrating a Service Registry (Nacos)
Add the Nacos discovery starter:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>Enable discovery on the main class and configure the address in application.yml:
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848In route definitions replace a fixed URI with the load‑balanced form lb://service-name (e.g., lb://gateway-provider). The built‑in LoadBalancerClientFilter will resolve the service from Nacos and apply load‑balancing.
Dynamic Routing via Nacos Config Center
Add the Nacos config starter:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>In bootstrap.yml specify Nacos as the configuration source:
spring:
application:
name: cloud-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yamlCreate a dataId cloud-gateway.yaml in the public namespace of Nacos containing the gateway routes (same format as in application.yml). When the dataId is updated, the gateway reloads routes automatically, achieving “single‑point modification, multi‑instance effect”.
Custom Global Exception Handling
When a downstream service is unavailable, Spring Cloud Gateway returns a default error page. To provide a friendly JSON response, implement ErrorWebExceptionHandler:
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
CommonResponse result = new CommonResponse("500", ex.getMessage(), null);
return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
} catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}Now the gateway returns a JSON payload such as {"code":"500","msg":"..."} instead of an HTML error page.
Summary
Why a gateway is needed and its basic functions.
Step‑by‑step creation of a Spring Cloud Gateway project.
Concepts of Predicate (route matching) and Filter (request/response processing).
Built‑in filters and how to write custom local and global filters.
Integration with Nacos for service discovery and load‑balancing.
Dynamic routing using Nacos Config Center for one‑place configuration.
Custom global exception handling to return friendly JSON errors.
Further deep‑dive articles will cover advanced topics and real‑world use cases.
The source code is uploaded to GitHub. Reply with the keyword 9528 to the public account "Code Monkey Technical Column" to get the link.
If this article helped you, please like, watch, share, and bookmark – your support keeps the series going. Follow the public account to join the technical discussion group.
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.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.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.
