Spring Cloud Gateway with Spring WebFlux

Spring Cloud Gateway is the library that allows you to build API gateways on top of Spring WebFlux. It provides a simple and effective way to route requests, apply filters, and manage cross-cutting concerns reactively. This article will guide you through demonstrating how to set up Spring Cloud Gateway with a simple microservice and route requests through the gateway to the microservice.

Spring Cloud Gateway routes incoming requests to downstream services based on various predicates and filters. It allows you to apply custom logic to the request and response flows using filters. This setup can be particularly useful in microservices architecture, where it acts as the entry point for external requests and forwards them to the appropriate microservices.

Project Overview:

We will create two main components:

  • Gateway Service: This service will use Spring Cloud Gateway to route incoming requests to downstream services. It will be configured to handle requests, apply filters, and manage cross-cutting concerns.
  • Example Microservice: This is a simple microservice that will serve as the target for the requests routed through the gateway.

Implementation of Spring Cloud Gateway with Spring WebFlux

Create the Gateway Project

Step 1: Create the Spring Project

We will create the new spring reactive project for the gateway service and add the below dependencies into the project.

Dependencies

  • Spring Reactive Web
  • Spring Gateway
  • Lombok
  • Spring DevTools

After creating the project, structure will be like below.


Step 2: Configure the Application properties

Open the application.properties file and rename into application.yml to add the gateway configuration.

server:
port: 8088

spring:
application:
name: gateway-service
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
routes:
- id: example_route
uri: http://localhost:8081
predicates:
- Path=/example/**
filters:
- AddRequestHeader=X-Request-Example, Gateway-Request
- AddResponseHeader=X-Response-Example, Gateway-Response


Step 3: Create the CustomFilter class

We will now create the CustomFilter class to demonstrate how to send request and responses in the application.

Go to src > main > java > org.example.webfluxapi > filters > CustomFilter and put the below code.

Java
package org.example.webfluxapi.filters;


import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {

    public CustomFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // Pre-processing
            if (!exchange.getRequest().getHeaders().containsKey("X-Request-Id")) {
                exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                return exchange.getResponse().setComplete();
            }

            // Post-processing
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                exchange.getResponse().getHeaders().add("X-Response-Default-Foo", "Default-Bar");
            }));
        };
    }

    public static class Config {
        // Put the configuration properties for your filter here
    }
}


Step 4: Create the GatewayConfig class

We will create the GatewayConfig class to define the routes and filters of the application.

Go to src > main > java > org.example.webfluxapi > config > GatewayConfig and put the below code.

Java
package org.example.webfluxapi.config;


import org.example.webfluxapi.filters.CustomFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder, CustomFilter customFilter) {
        return builder.routes()
                .route("example_route", r -> r.path("/example/service/**")
                        .filters(f -> f.filter(customFilter.apply(new CustomFilter.Config())))
                        .uri("http://localhost:8081"))
                .build();
    }
}


Step 5: Main Class

No changes are required in the main class of the project.

Java
package org.example.webfluxapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebfluxApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebfluxApiApplication.class, args);
    }

}


pom.xml:

XML
<?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.2.7-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>webflux-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>webflux-api</name>
    <description>webflux-api</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2023.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>

</project>


Step 6: Run the application

Once complete the project, we will now run the application and then it will start at port 8088.


Create the Example-microservice

Step 1: Create the Spring Project

Now, we will create one new spring reactive project for the example-microservice service by adding the below dependencies into the project.

Dependencies:

  • Spring Reactive Web
  • Lombok
  • Spring DevTools

Folder Structure:


Step 2: Configure the Application properties

Open the application.properties file and rename into application.yml to add the server port configuration.

server:
port: 8081

spring:
application:
name: example-microservice


Step 3: Create the ExampleController class

We will create the ExampleController class to define the simple REST endpoint of the Spring application.

Go to src > main > src > org.example.examplemicroservice > ExampleController and put the below code.

Java
package org.example.examplemicroservice;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class ExampleController {

    @GetMapping("/example/service")
    public Mono<String> exampleEndpoint() {
        return Mono.just("Hello from the Example Microservice!");
    }
}


Step 4: Main Class

No changes are required in the main class.

Java
package org.example.examplemicroservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ExampleMicroserviceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ExampleMicroserviceApplication.class, args);
    }

}


pom.xml:

XML
<?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.3.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>example-microservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>example-microservice</name>
    <description>example-microservice</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


Step 5: Run the application

Once complete the project, we will now run the application and then it will start at port 8081.


Testing the API Endpoint Gateway

After successfully running both the gateway and microservices, we will now test the endpoints:

API Endpoint:

GET http://localhost:8080/example/service

Output:



Contact Us