Spring Boot Microservices with Spring Cloud API Gateway Global Filter Example

In this tutorial, we will create two Spring Boot microservices and set up an API Gateway that uses a global filter. We'll use the latest Spring Boot version 3.2+ and Spring Cloud 2023.x. This guide is intended for beginners and includes detailed explanations for each step.
  1. Eureka Server: Acts as a central registry where microservices register themselves.
  2. Microservices: Register with Eureka Server and discover other registered services via Eureka.
  3. API Gateway: Uses Eureka Server for service discovery, routes requests to the appropriate microservice, and applies global filters to modify requests and responses.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up the Eureka Server

1.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Eureka Server

1.2 Configure application.properties

Set up the application properties for the Eureka Server.

server.port=8761
spring.application.name=eureka-server

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Explanation:

  • server.port=8761: Sets the port for the Eureka Server.
  • spring.application.name=eureka-server: Names the application.
  • eureka.client.register-with-eureka=false: Indicates that the Eureka Server itself should not try to register with another Eureka Server.
  • eureka.client.fetch-registry=false: Indicates that the Eureka Server should not attempt to fetch registry information from another Eureka Server.

1.3 Enable Eureka Server

Add the @EnableEurekaServer annotation to the main application class.

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

Explanation:

  • @EnableEurekaServer: Enables the Eureka Server functionality in your Spring Boot application.

Step 2: Set Up service-a

2.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Eureka Discovery Client

2.2 Configure application.properties

Set up the application properties for service-a.

server.port=8081
spring.application.name=service-a

eureka.client.service-url.default-zone=http://localhost:8761/eureka/

Explanation:

  • server.port=8081: Sets the port for service-a.
  • spring.application.name=service-a: Names the application.
  • eureka.client.service-url.default-zone=http://localhost:8761/eureka/: Specifies the Eureka Server URL for service registration.

2.3 Enable Eureka Client

Add the @EnableDiscoveryClient annotation to the main application class.

package com.example.servicea;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ServiceAApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }
}

Explanation:

  • @EnableDiscoveryClient: Indicates that this application should register with a Eureka Server for service discovery.

2.4 Create a Controller

Create a controller to handle requests.

package com.example.servicea;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceAController {

    @GetMapping("/service-a")
    public String getServiceA() {
        return "Response from Service A";
    }
}

Explanation:

  • @RestController: Marks this class as a REST controller.
  • @GetMapping("/service-a"): Maps GET requests to /service-a to this method.

Step 3: Set Up service-b

3.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Eureka Discovery Client

3.2 Configure application.properties

Set up the application properties for service-b.

server.port=8082
spring.application.name=service-b

eureka.client.service-url.default-zone=http://localhost:8761/eureka/

Explanation:

  • server.port=8082: Sets the port for service-b.
  • spring.application.name=service-b: Names the application.
  • eureka.client.service-url.default-zone=http://localhost:8761/eureka/: Specifies the Eureka Server URL for service registration.

3.3 Enable Eureka Client

Add the @EnableDiscoveryClient annotation to the main application class.

package com.example.serviceb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ServiceBApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
    }
}

Explanation:

  • @EnableDiscoveryClient: Indicates that this application should register with a Eureka Server for service discovery.

3.4 Create a Controller

Create a controller to handle requests.

package com.example.serviceb;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceBController {

    @GetMapping("/service-b")
    public String getServiceB() {
        return "Response from Service B";
    }
}

Explanation:

  • @RestController: Marks this class as a REST controller.
  • @GetMapping("/service-b"): Maps GET requests to /service-b to this method.

Step 4: Set Up the API Gateway with Global Filter

4.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Cloud Gateway
  • Eureka Discovery Client

4.2 Configure application.properties

Set up the application properties for the API Gateway.

server.port=8080
spring.application.name=api-gateway

eureka.client.service-url.default-zone=http://localhost:8761/eureka/
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

Explanation:

  • server.port=8080: Sets the port for the API Gateway.
  • spring.application.name=api-gateway: Names the application.
  • eureka.client.service-url.default-zone=http://localhost:8761/eureka/: Specifies the Eureka Server URL for service registration.
  • spring.cloud.gateway.discovery.locator.enabled=true: Enables the Discovery Locator to automatically map routes.
  • spring.cloud.gateway.discovery.locator.lower-case-service-id=true: Converts service IDs to lowercase.

4.3 Enable Eureka Client and Gateway

Enable Eureka client and gateway functionality by adding the @EnableDiscoveryClient annotation in the main application class.

package com.example.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}

Explanation:

  • @EnableDiscoveryClient: Indicates that this application should register with a Eureka Server for service discovery.

4.4 Create a Global Filter

Create a global filter to log requests and responses.

package com.example.apigateway;

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Configuration
public class GlobalFiltersConfiguration {

    @Bean
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> {
            System.out.println("Global Pre Filter executed");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("Global Post Filter executed");
            }));
        };
    }
}

Explanation:

  • @Configuration: Indicates that this class contains Spring configuration.
  • @Bean: Marks this method as a bean producer.
  • GlobalFilter: Interface for creating global filters in Spring Cloud Gateway.
  • The filter logs messages before and after the request is processed by the downstream service.

Step 5: Run the Microservices

  1. Start the Eureka Server: Run the EurekaServerApplication class.
  2. Start service-a: Run the ServiceAApplication class.
  3. Start service-b

: Run the ServiceBApplication class. 4. Start the API Gateway: Run the ApiGatewayApplication class.

Step 6: Test the Communication and Global Filter

Open your browser or use a tool like Postman to test the endpoints through the API Gateway:

  • service-a: http://localhost:8080/service-a/service-a
  • service-b: http://localhost:8080/service-b/service-b

The response from service-a and service-b should include the respective messages defined in their controllers. Check your console logs to see the messages from the global filter.

Conclusion

You have successfully set up two Spring Boot microservices and an API Gateway with a global filter using Eureka Server for service discovery. The API Gateway dynamically discovers the services registered with Eureka and routes the requests appropriately, applying the global filter to all requests. This setup demonstrates how to use Spring Cloud components to create a scalable and maintainable microservices architecture. This setup can be expanded to include more microservices and more complex filtering and routing scenarios.


Comments