Cloud Native 25 min read

Comprehensive Guide to Spring Cloud Gateway: Architecture, Configuration, and Custom Extensions

This article explains why an API gateway is essential in micro‑service architectures, introduces Spring Cloud Gateway’s core features, walks through project setup, routing predicates, filters, integration with Nacos for service discovery and dynamic routing, and demonstrates custom global error handling.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Comprehensive Guide to Spring Cloud Gateway: Architecture, Configuration, and Custom Extensions

Hello everyone, I’m Chen.

This article introduces an important role in micro‑service architecture: the gateway. Since the Alibaba ecosystem does not provide a gateway yet, we choose Spring Cloud Gateway, the "son" of Spring Cloud.

Readers who have already read this article can skip the following sections.

Table of contents:

Why Do We Need a Gateway?

In a traditional monolithic architecture there is only one service exposed to the client, but in a micro‑service architecture a system is split into many services. Without a gateway the client would have to remember the address of each service.

Without a gateway, the following problems usually appear:

Clients need to request many different services, increasing code and configuration complexity.

Authentication becomes complicated because each service must handle it independently.

Cross‑origin requests appear and are often hard to handle.

Basic Functions of a Gateway

The gateway is the entry point for all micro‑services. Routing is the most basic function, but it also provides authentication, authorization, circuit breaking, rate limiting, logging, monitoring, etc.

The above scenarios will be detailed in later articles; they are not the focus of this post.

Why Choose Spring Cloud Gateway?

In version 1.x the default gateway was Zuul; however Zuul’s upgrade kept being postponed. Spring Cloud therefore created its own gateway – Spring Cloud Gateway.

Choosing the "son" Spring Cloud Gateway makes sense because many ideas are borrowed from Zuul, and its functionality and performance are superior.

Key reason:

Spring Cloud Gateway is built on Spring Boot 2.x, Spring WebFlux, and Project Reactor.

Integration with Spring Boot ensures compatibility and good performance.

Key Terminology in Spring Cloud Gateway

Route : The basic building block of the gateway, consisting of an ID, target URI, a set of predicates, and a set of filters. If all predicates evaluate to true, the route matches.

Predicate : Inspired by Java 8’s Predicate interface, it allows matching any part of an HTTP request such as headers or parameters.

Filter : Can modify the request or response before or after the request is forwarded.

How to Build a Gateway

Why show this diagram?

You must follow the version shown in the diagram, otherwise unexpected bugs may occur – I have experienced this.

Create a project named cloud-gateway9023 and add the following dependency:

<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Note: Remove the spring-boot-starter-web dependency, otherwise the application will fail to start.

The project is now set up; the rest of the configuration will be introduced later.

What Is a Predicate?

Predicate comes from Java 8’s functional interface. It receives an input and returns a boolean. It provides default methods for composing complex logic (and, or, not). It can be used for request parameter validation or detecting data changes.

Spring Cloud Gateway ships many built‑in predicates located in the package org.springframework.cloud.gateway.handler.predicate. The built‑in predicates are shown in the following image:

There are 11 built‑in predicates; the official documentation explains how to configure them.

Official documentation: https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/

Below is an example of configuring a weight predicate:

spring:
  cloud:
    gateway:
      ## routes
      routes:
        ## id must be unique
        - id: gateway-provider_1
          uri: http://localhost:9024
          ## predicates
          predicates:
            # Path Route Predicate Factory – matches /gateway/provider/**
            - Path=/gateway/provider/**
            # Weight Route Predicate Factory – 80% weight in group1
            - Weight=group1,8
        - id: gateway-provider_2
          uri: http://localhost:9025
          predicates:
            - Path=/gateway/provider/**
            # Weight Route Predicate Factory – 20% weight in group1
            - Weight=group1,2

The route configuration consists of the following components: id: unique identifier of the route. uri: the target address. predicates: one or more predicate definitions.

Gateway predicate names follow the pattern xxxRoutePredicateFactory. For example, the weight predicate class is WeightRoutePredicateFactory, and the configuration key is simply Weight.

When multiple routes match, the first defined route wins unless a weight predicate is used, in which case traffic is split according to the weight.

What Is a Filter?

The concept of a filter is familiar from Spring MVC. In the gateway, filters have a similar lifecycle.

Gateway lifecycle:

PRE : Executed before the request is routed – useful for authentication, service selection, logging, etc.

POST : Executed after the request has been routed – useful for adding response headers, collecting metrics, etc.

Filters can be applied in two scopes:

GatewayFilter : Applied to a specific route or a group of routes (configured in the route definition).

GlobalFilter : Applied to all routes automatically.

GatewayFilter (Local Filter)

Spring Cloud Gateway provides many built‑in local filters (see the image below). They only take effect when attached to a route.

Example: using AddResponseHeaderGatewayFilterFactory to add a response header.

spring:
  cloud:
    gateway:
      routes:
        - id: gateway-provider_1
          uri: http://localhost:9024
          predicates:
            - Path=/gateway/provider/**
          filters:
            - AddResponseHeader=X-Response-Foo,Bar

After sending a request, the response contains the header X-Response-Foo=Bar (see screenshot).

More filter configurations can be found in the official documentation: https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#gatewayfilter-factories

Sometimes built‑in filters are not enough, so we can create a custom local filter. Example scenario: authorize requests based on a token header or query parameter; reject the request with 401 if the token is missing.

/**
 * The class name must end with xxxGatewayFilterFactory
 * TODO: implement actual authorization logic
 */
@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 custom filter in the route definition:

spring:
  cloud:
    gateway:
      routes:
        - id: gateway-provider_1
          uri: http://localhost:9024
          predicates:
            - Path=/gateway/provider/**
          filters:
            - AddResponseHeader=X-Response-Foo,Bar
            - Authorize=true

When accessing http://localhost:9023/gateway/provider/port without a token, the gateway returns 401; with ?token=abcdcdecd-ddcdeicd12 it succeeds.

How to Integrate a Service Registry?

The demo above uses fixed URIs, which has drawbacks: IP changes require configuration updates, and load balancing across a cluster is impossible.

Service IP changes force manual URI updates.

Clustered services cannot be load‑balanced.

Integrating a registry (Nacos) allows the gateway to discover services automatically and perform load balancing.

Add the Nacos dependency:

<!--nacos registry-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

Enable discovery in the main class (see screenshot).

Configure Nacos address in application.yml:

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

In the route definition replace a fixed URI with the load‑balanced form lb://service-name:

spring:
  cloud:
    gateway:
      routes:
        - id: gateway-provider_1
          uri: lb://gateway-provider
          predicates:
            - Path=/gateway/provider/**
          filters:
            - AddResponseHeader=X-Response-Foo,Bar

The lb prefix tells Spring Cloud to obtain the service address from Nacos and apply load balancing.

How to Implement Dynamic Routing?

Hard‑coding routes in the configuration file means any change requires a redeployment. By storing gateway configuration in Nacos Config Center, a single change takes effect immediately.

Add the Nacos Config dependency:

<!-- nacos config center dependency -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

In bootstrap.yml specify the config server address and file extension:

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

Create a dataId cloud-gateway.yaml in Nacos’s public namespace containing the gateway routes. Once the data is published, the gateway will automatically reload the routes without restarting.

How to Customize Global Exception Handling?

When a downstream service is unavailable, Spring Cloud Gateway returns an HTML error page, which is not suitable for a front‑end/back‑end separation architecture.

Implement a class that implements ErrorWebExceptionHandler and returns JSON responses:

/**
 * Global exception handler for the gateway.
 * @Order(-1) ensures it runs after the default handler.
 */
@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]);
            }
        }));
    }
}

After adding this handler, the gateway returns a JSON payload like {"code":"500","msg":"..."} instead of an HTML page.

The JSON format can be customized according to the architecture’s needs.

Summary

Spring Cloud Gateway has been covered with the following points:

Why a gateway is needed and its basic functions.

How to build a gateway from scratch.

The concept of Predicate (route matching).

Filter concepts, built‑in filters, and how to create custom filters.

Integrating Nacos for service discovery and load balancing.

Using Nacos as a configuration center for dynamic routing.

Custom global exception handling.

Further, deeper and practical topics will be introduced in the next article.

The project source code is uploaded to GitHub. Reply with the keyword 9528 in the public account "Code Monkey Technical Column" to obtain it.

Final Word (Please Support)

If this article helped you, please like, view, share, and bookmark it – your support is the biggest motivation for me!

My knowledge‑sharing community (Knowledge Star) offers limited‑time coupons: 30 CNY off for a 89 CNY subscription. It includes Spring full‑stack practice series, massive data sharding, DDD micro‑service practice, source‑code deep‑dive, and more. Prices increase by 20 CNY per additional series.

Follow the public account "Code Monkey Technical Column" for exclusive fan benefits. Reply "join group" to get into the technical discussion group.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Microservicesload balancingapi-gatewayNacosSpring BootSpring Cloud GatewayCustom Filters
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.