DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Using Spring Cloud Gateway and Discovery Service for Seamless Request Routing
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How to Develop Microservices With Spring Cloud and Netflix Discovery
  • Full-Duplex Scalable Client-Server Communication with WebSockets and Spring Boot (Part I)

Trending

  • Unlocking AI Coding Assistants Part 3: Generating Diagrams, Open API Specs, And Test Data
  • A Simple, Convenience Package for the Azure Cosmos DB Go SDK
  • The Role of Functional Programming in Modern Software Development
  • Teradata Performance and Skew Prevention Tips
  1. DZone
  2. Software Design and Architecture
  3. Microservices
  4. How To Implement a Gateway With Spring Cloud

How To Implement a Gateway With Spring Cloud

Learn how to implement a gateway component for a microservice system with the Spring Cloud Gateway package.

By 
Mario Casari user avatar
Mario Casari
·
Jul. 25, 24 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
9.5K Views

Join the DZone community and get the full member experience.

Join For Free

A microservice system could have a high number of components with complex interactions. It is important to reduce this complexity, at least from the standpoint of the clients interacting with the system. A gateway hides the microservices from the external world. It represents a unique entrance and implements common cross requirements. In this article, you will learn how to configure a gateway component for a Spring Boot application, using the Spring Cloud Gateway package.

Spring Cloud Gateway

Spring Cloud provides a gateway implementation by the Spring Cloud Gateway project. It is based on Spring Boot, Spring WebFlux, and Reactor. Since it is based on Spring WebFlux, it must run on a Netty environment, not a usual servlet container.

The main function of a gateway is to route requests from external clients to the microservices. Its main components are:

  • Route: This is the basic entity. It is configured with an ID, a destination URI, one or more predicates, and filters.
  • Predicate: This is based on a Java function predicate. It represents a condition that must be matched on head or request parameters.
  • Filter: It allows you to change the request or the response.

We can identify the following sequence of events:

  • A client makes a call through the gateway.
  • The gateway decides if the request matches a configured route.
  • If there is a match, the request is sent to a gateway web handler.
  • The web handler sends the request to a chain of filters that can execute logic relative to the request or the response, and operate changes on them.
  • The target service is executed.

Spring Cloud Gateway Dependencies

To implement our Spring Boot application as a gateway we must first provide the spring-cloud-starter-gateway dependency after having defined the release train as in the configuration fragment below:

XML
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2023.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


XML
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
      ...
    </dependencies>


Spring Cloud Gateway Configuration

We can configure our gateway component using the application.yaml file. We can specify fully expanded arguments or shortcuts to define predicates and filters. In the first case, we define a name and an args field. The args field can contain one or more key-value pairs:

YAML
 
spring:
  cloud:
    gateway:
      routes:
      - id: route-example
        uri: https://example.com
        predicates:
        - name: PredicateExample
          args:
            name: predicateName
            regexp: predicateRegexpValue


In the example above, we define a route with an ID value of "route-example", a destination URI "https://example.com," and a predicate with two args, "name" and "regexp."

With the shortcut mode, we write the predicate name followed by the "=" character, and then a list of names and values separated by commas. We can rewrite the previous example by the following:  

YAML
 
spring:
  cloud:
    gateway:
      routes:
      - id: route-example
        uri: https://example.com

        predicates:
        - Cookie=predicateName,predicateRegexpValue


A specific factory class implements each predicate and filter type. There are several built-in predicate and filter factories available. The Cookie predicate shown above is an example. We will list some of them in the following sub-sections.

Predicate Built-In Factories

  • The After Predicate Factorymatches requests that happen after a specific time.
 
After=2007-12-03T10:15:30+01:00 Europe/Paris


  • The Before Predicate Factorymatches requests that happen before a specific time.
 
Before=2007-12-03T10:15:30+01:00 Europe/Paris


  • The Method Route Predicate Factoryspecifies the HTTP method types to match.
 
Method=GET,POST


Filter Built-In Factories

  • The AddRequestHeader GatewayFilter Factoryallows the addition of an HTTP header to the request by its name and value.
    • AddRequestHeader=X-Request-Foo, Bar
  • The AddRequestParameter GatewayFilter Factoryallows the addition of a parameter to the request by its name and value.
    • AddRequestParameter=foo, bar
  • The AddResponseHeader GatewayFilter Factoryallows the addition of an HTTP header to the request by its name and value.
    • AddResponseHeader=X-Response-Foo, Bar

To implement a custom predicate or filter factory, we have to provide an implementation of a specific factory interface. The following sections show how.

Custom Predicate Factories

To create a custom predicate factory we can extend the AbstractRoutePredicateFactory, an abstract implementation of the RoutePredicateFactory interface. In the example below we define an inner static class Configuration, to pass its properties to the apply method and compare them to the request. 

Java
 
@Component
public class CustomPredicateFactory extends AbstractRoutePredicateFactory<CustomPredicateFactory.Configuration> {

    public CustomPredicateFactory() {
        super(Configuration.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Configurationconfig) {
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            //compare request with config properties
            return matches(config, request);
        };
    }

    private boolean matches(Configuration config, ServerHttpRequest request) {
        //implement matching logic
        return false;
    }

    public static class Configuration{
        //implement custom configuration properties
    }
}


Custom Filter Factories

To create a custom filter factory we can extend the AbstractGatewayFilterFactory, an abstract implementation of the GatewayFilterFactory interface. In the examples below, you can see a filter factory that modifies the request and another one that changes the response, using the properties passed by a Configuration object.

Java
 
@Component
public class PreCustomFilterFactory extends AbstractGatewayFilterFactory<PreCustomFilterFactory.Configuration> {

    public PreCustomFilterFactory() {
        super(Configuration.class);
    }

    @Override
    public GatewayFilter apply(Configuration config) {
        return (exchange, chain) -> {
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //use builder to modify the request
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }

    public static class Configuration {
        //implement the configuration properties
    }

}

@Component
public class PostCustomFilterFactory extends AbstractGatewayFilterFactory<PostCustomFilterFactory.Configuration> {

    public PostCustomFilterFactory() {
        super(Configuration.class);
    }

    @Override
    public GatewayFilter apply(Configuration config) {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                    //Change the response
            }));
        };
    }

    public static class Configuration {
        //implement the configuration properties
    }

}


Spring Cloud Gateway Example

We will show a practical and simple example to see how the gateway works in a real scenario. You will find a link to the source code at the end of the article. The example is based on the following stack:

  • Spring Boot: 3.2.1
  • Spring Cloud: 2023.0.0 
  • Java 17

We consider a minimal microservice system that implements a library with only two services: a book service and an author service. The book service calls the author service to retrieve an author's information by passing an authorName parameter. The implementation of the two applications is based on an embedded in-memory H2 database and uses JPA ORM to map and query the Book and Author tables. From the standpoint of this demonstration, the most important part is the /getAuthor REST endpoint exposed by a BookController class of the Book service:

Java
 
@RestController
@RequestMapping("/library")
public class BookController {

    Logger logger = LoggerFactory.getLogger(BookService.class);

    @Autowired
    private BookService bookService;

    @GetMapping(value = "/getAuthor", params = { "authorName" })
    public Optional<Author> getAuthor(@RequestParam("authorName") String authorName) {
        return bookService.getAuthor(authorName);
    }
}


The two applications register themselves in an Eureka discovery server and are configured as discovery clients. The final component is the gateway. The gateway should not register itself with the service discovery server. This is because it is called only by external clients, not internal microservices. On the other hand, it can be configured as a discovery client to fetch the other services automatically and implement a more dynamic routing. We don't do it here, though, to keep things simple.

In this example, we want to show two things:

  • See how the routing mechanism by the predicate value works
  • Show how to modify the request by a filter, adding a header

The gateway's configuration is the following:

Java
 
spring:
   application:
      name: gateway-service
   cloud:
      gateway:
         routes:
            - id: add_request_header_route
              uri: http://localhost:8082
              predicates:
                 - Path=/library/**
              filters:
                 - AddRequestHeader=X-Request-red, red


We have defined a route with id "add_request_header_route," and a URI value of "http://localhost:8082," the base URI of the book service. We then have a Path predicate with a "/library/**" value. Every call starting with "http://localhost:8080/library/" will be matched and routed toward the book's service with URIs starting with "http://localhost:8082/library/."

Running the Example

To run the example you can start each component by executing "mvn spring-boot:run" command from the component's base directory. You can then test it by executing the "http://localhost:8080/library/getAuthor?authorName=Goethe" URI. The result will be a JSON value containing info about the author. If you check the browser developer tools you will also find that an X-Request-red header has been added to the request with a value of "red."

Conclusion

Implementing a gateway with the Spring Cloud Gateway package is the natural choice in the Spring Boot framework. It reduces the complexity of a microservice environment by placing a single facade component in front of it. It also gives you great flexibility in implementing cross-cutting concerns like authentication, authorization, aggregate logging, tracing, and monitoring.

You can find the source code of the example of this article on GitHub.

Spring Cloud Dependency Java (programming language) Spring Boot microservice

Published at DZone with permission of Mario Casari. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Using Spring Cloud Gateway and Discovery Service for Seamless Request Routing
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How to Develop Microservices With Spring Cloud and Netflix Discovery
  • Full-Duplex Scalable Client-Server Communication with WebSockets and Spring Boot (Part I)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!