Cloud Native 21 min read

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.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Master Spring Cloud Gateway: Routing, Custom Predicates & Filters Explained

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:

Gateway architecture diagram
Gateway architecture diagram

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: 80

Define 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: true

Custom 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: zhangsan

Filters

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,api

Global 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.

Predicate types illustration
Predicate types illustration
CORS response headers
CORS response headers
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.

MicroservicesroutingCORSSpring Cloud GatewayfilterCustom Predicate
Su San Talks Tech
Written by

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.

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.