DEV Community

Cover image for Spring Cloud Gateway: Basic Example
Eduardo
Eduardo

Posted on

Spring Cloud Gateway: Basic Example

Spring Cloud Gateway (also known as a gateway or Edge Service) is a dynamic routing server. In other words, it allows us to have a centralized access point to our Microservices. Additionally, we can extend its functionality by adding filters or predicates, among other things.

1. Spring Cloud Gateway Configuration

We create a project using Spring Initializr or our favorite IDE, making sure to select the following dependencies:

  • Eureka Discovery Client
  • Gateway

This project will also be registered by Eureka Server, so we add the following configuration:

@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we configure the application name and the port where it will be deployed. We can do this in a properties or yml file.

spring:
  application:
    name: gateway-server-service
server:
  port: 8090
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
Enter fullscreen mode Exit fullscreen mode

Just like the previous configuration, Spring Cloud Gateway can be configured in a properties file or a yml file, but for easier reading, I will show the yml configuration here.

2. Routes

The minimum essential configuration for the Edge or Gateway is the routes. These routes will redirect requests from the Gateway to the service that will answer the request.

The following is an example of a basic route configuration in YAML:

spring:
  cloud:
    gateway:
      routes:
        - id: employees
          uri: lb://employees
          predicates:
            - Path=/api/employees/**
          filters:
            - StripPrefix=2
Enter fullscreen mode Exit fullscreen mode
  • id: Unique identifier of the route.
  • uri: Address where the Edge will look for the service to redirect. It must start with lb (Load Balancer) if using service discovery like Eureka.
  • predicates: Series of rules that must be met for the redirection.
  • filters: Help us work with the request and response passing through the declared route.

In the case of the example, it is routing to a Microservice called employees (or empleados), and if we want to get a response from it, we can use the Spring Cloud Gateway pointing to the Edge port and not to the Microservice Port directly.

2.1 Predicates

Predicates are a list of rules that must be met for the redirection to the service to be successful.

Predicates can be of various types: by redirection address, by header, by method type, query, or cookie.

List of possible examples:

predicates:
  - Path=/api/products/**
  - Header=token, \d+
  - Method=GET, POST
  - Query=color
  - Cookie=color, blue
Enter fullscreen mode Exit fullscreen mode

Meaning of the predicates:

  • Path: Allows redirection of requests that match the declared path.
  • Header: Checks that the declared header comes within the request with the indicated value (regex capable).
  • Method: Indicates that it only receives the declared HTTP methods.
  • Query: Indicates that the search parameter must appear in the URL query.
  • Cookie: Checks that the indicated cookie is present.

2.2 Spring Cloud Gateway Factory Filters

There are filters that help us manipulate the request and response. This means you can add information to the request or response within the headers or as a parameter in the request.

The way to add it is in the application.yml file within the configuration section of the desired path in a comma-separated key-value format.

filters:
  - AddRequestHeader=token-request, 123456
  - AddResponseHeader=token-response, 12345678
  - SetResponseHeader=Content-Type, text/plain
  - AddRequestParameter=name, andres
Enter fullscreen mode Exit fullscreen mode

2.3 Spring Cloud Gateway Global Filters

There is a way to make a filter pass through all our requests and responses without having to declare it one by one. In the following example, we are going to add a header to the request and a cookie to the response of all requests that pass through the Spring Cloud Gateway.

package com.funcionaenmimaquina.springboot.app.gateway.filters;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Optional;

@Component
public class ExampleGlobalFilter implements GlobalFilter, Ordered {

    private final Logger logger = LoggerFactory.getLogger(ExampleGlobalFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        logger.info("Executing request filter");

        // Modifying the request
        exchange.getRequest().mutate().headers(h -> h.add("environment", "dev"));

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            logger.info("Executing response filter");

            // Modifying the response
            Optional.ofNullable(exchange.getRequest().getHeaders().getFirst("environment")).ifPresent(value -> {
                exchange.getResponse().getHeaders().add("env", value);
            });

            exchange.getResponse().getHeaders().setContentType(MediaType.TEXT_PLAIN);
        }));
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
Enter fullscreen mode Exit fullscreen mode

To understand the previous example, we must highlight several things:

  1. It implements the GlobalFilter and Ordered interfaces.
  2. The overridden method getOrder is used to give an order in case of having more global filter classes.
  3. In the overridden filter method, all the logic to interact with our request and response is written.
  4. Line 24 shows the section where you can manipulate the request, in this case adding a header.
  5. Line 27 shows how to manipulate a response (using .then(Mono.fromRunnable(...))).
  6. Line 31 adds a header to the response if the condition that a specific header exists is met.
  7. Line 34 manipulates the response to take a plain text format.

2.4 Filter Factory (Custom Gateway Filter)

If we do not want the Filter to pass through all requests and responses, but only through those we specify, we can create a custom filter as follows.

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

    private final Logger logger = LoggerFactory.getLogger(ExampleGatewayFilterFactory.class);

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            logger.info("Zone to modify the request");

            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                logger.info("Zone to modify the response");

                Optional.ofNullable(config.cookieValue).ifPresent(cookieVal -> {
                    exchange.getResponse().addCookie(ResponseCookie.from(config.cookieName, cookieVal).build());
                });
            }));
        };
    }

    @Override
    public String name() {
        return "ExampleCookie";
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("cookieName", "cookieValue");
    }

    public static class Config {
        private String cookieName;
        private String cookieValue;

        // Getters and Setters
        public String getCookieValue() { return cookieValue; }
        public void setCookieValue(String cookieValue) { this.cookieValue = cookieValue; }
        public String getCookieName() { return cookieName; }
        public void setCookieName(String cookieName) { this.cookieName = cookieName; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage in YML:

filters:
  - StripPrefix=2
  - ExampleCookie=user, myName
Enter fullscreen mode Exit fullscreen mode

What the code above does is add a Cookie to the response of the request with the values described in the yml file, and the following should be highlighted:

By convention, the class name must end in FilterFactory.

It extends the AbstractGatewayFilterFactory class.

You need to use a configuration class (inner class Config); this configuration class will store the values sent from the yml file.

The functionality is coded inside the apply method.

The return value of the name() method must match the key in the yml file (or default to the class name prefix).

The shortcutFieldOrder method indicates the order in which the values will be assigned from the YAML short notation.

Now this filter will only be applied to the requests configured within the application.yml file.

Conclusions

Now you know what Spring Cloud Gateway is for and what you can achieve by configuring it. You know how to configure predicates and filters to extend its functionality. Don't forget to practice to be able to use it in the professional world.

Related Links

Top comments (0)