Spring API Gateway

👉 Learn Spring Cloud concepts.


8. API Gateway

Project ref: b7-api-gateway

  • Purpose / Feature
    • All requests will be routed via API Gateway.
    • This gives us flexibility to implement all common features at one place.
    • Build on top of Spring WebFlux (Reactive Approach).
    • Provide cross cutting concerns
      • Security
      • Monitoring / Metrics
    • Features:
      • Match routes to any request attribute to route to right service.
      • Allows to define Predicates (Matcher) & Filters (eg. Authentication, Authorization, Logging etc.).
      • Integrates with Spring Cloud Discovery client to load balancing to the service for which the request is received.
      • Path rewriting.
  • Steps
    • Step-1: Ensure following dependencies are added.
      • Eureka Discovery Client
      • Reactive API Gateay
    • Step-2: Enable API gateway client discovery in application.properties
    • Step-3: Now, we can access services with the Service name registered in Eureka server and path to controller API.
      • http://localhost:8765/B5-CURRENCY-CONVERSION-SERVICE-OPENFEIGN/currency-conversion-feign/from/UsD/to/iNr/quantity/100
    • Step-4: Add property to accept service name in lower case
      • http://localhost:8765/b5-currency-conversion-service-openfeign/currency-conversion-feign/from/UsD/to/iNr/quantity/100
  • Maven / External dependency
    • Required dependency. ```xml org.springframework.cloudspring-cloud-starter-gatewayorg.springframework.cloudspring-cloud-starter-netflix-eureka-client
  • Code / Config changes
    • Application Config:application.properties
          spring.application.name=b7-api-gateway
          server.port=8765
      
          # Start: Eureka client config
      
          # Map of availability zone to list of fully qualified URLs to communicate with eureka server. 
          # Each value can be a single URL or a comma separated list of alternative locations. 
          # Typically the eureka server URLs carry protocol,host,port,context and version information if any. 
          # Example: https://ec2-256-156-243-129.compute-1.amazonaws.com:7001/eureka/ 
          eureka.client.service-url.defaultZone=http://localhost:8761/eureka
      
          # Enables the Eureka health check handler.
          eureka.client.healthcheck.enabled=true
      
          eureka.instance.lease-renewal-interval-in-seconds=60
          eureka.instance.lease-expiration-duration-in-seconds=60
      
          # End: Eureka client config
      
          # Start: Api Gateway
      
          # Flag that enables Discovery Client gateway integration.
          # This allows us to invoke service using service-name registered in Eureka
          # http://localhost:8765/B3-CURRENCY-EXCHANGE-SERVICE/currency-exchange/from/usd/to/inr
          spring.cloud.gateway.discovery.locator.enabled=true
      
          # Option to lower case serviceId in predicates and filters, defaults to false. 
          # Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/**
          spring.cloud.gateway.discovery.locator.lower-case-service-id=true
      
          # End: Api Gateway
      

Note: This is an important note.


9. Routes with spring cloud gateway

Project ref: b8-api-gateway-routes

  • Purpose / Feature
    • API Gateway Routers and Filters provides the option to intercept and process the requests.
    • It provides methods/APIs to match request on any attribute - method, header, cookie, path, host etc..
    • It allow to route specific URLs to desired service.
    • We can add HTTP headers & params in the request.
    • We can also rewrite the URL.
    • We can also implement common checks such as authentication, authorization, logging etc.
  • Steps
    • Step-1: Considering tht API Gateway is already implemented.
      • If not, refer section 8 above for API Gateway coniguration.
    • Step-2: Update application configuration in application.properties.
      • Disable gateway discovery locator configuration in application.properties to auto discover clients from Eureka server.
    • Step-3: Create a Configuration class.
      • Create a Bean of o.s.c.gateway.route.RouteLocator class with method accepting RouteLocatorBuilder class as argument.
      • Now using RouteLocatorBuilder instance, we can customize routes, e.g.
        • Route any URL to lb://<service-name-in-eureka> to redirect and do load-balancing.
        • Route to any external service http://httpbin.org/.
          • Request failing for HTTPS requests, requires SSL confguration.
        • Rewrite the requested URL to corresponfing inernal service URL using filters.
  • Maven / External dependency
    • Required dependency. ```xml org.springframework.cloudspring-cloud-starter-gatewayorg.springframework.cloudspring-cloud-starter-netflix-eureka-client
  • Code / Config changes
    • Configuratio:ApiGatewayConfiguration.java
      • imports
        • import org.springframework.cloud.gateway.route.RouteLocator;
        • import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
        • import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder;
      • Create a Bean of RouteLocator class. ```java /**
        • Configuration class to build custom routes and request customization. */ @Configuration public class ApiGatewayConfiguration {

          /**

          • Define routes.
          • @param routeLocatorBuilder
          • @return / @Bean RouteLocator getwayRoute(RouteLocatorBuilder routeLocatorBuilder) { /
            • Redirect request to external service. Also, optionally we can add http
            • headers and request params in the request. */ Builder routeLocator = routeLocatorBuilder.routes() .route((p) -> p.path(“/get”) .filters(f -> f.addRequestHeader(“MY-HEADER”, “MY-CUSTOM-HEADER”) .addRequestParameter(“MY-REQUEST-PARAM”, “MY-CUSTOM-REQUEST-PARAM”)) .uri(“http://httpbin.org:80/”));

            /**

            • Route URLs to load balancer and eureka service name */ routeLocator = routeLocator.route(p -> p.path(“/currency-exchange/”).uri(“lb://b3-currency-exchange-service”)) .route(p -> p.path(“/currency-conversion-feign/”) .uri(“lb://b5-currency-conversion-service-openfeign”));

            /**

            • Rewrite URL and copy the path */ routeLocator.route(p -> p.path(“/ccfs/**”) .filters(f -> f.rewritePath(“/ccfs/(?.*)", "/currency-conversion-feign/${segment}")) .uri("lb://b5-currency-conversion-service-openfeign"));

            return routeLocator.build(); } } ```

    • Application Config:application.properties
          spring.application.name=b8-api-gateway-routes
      
          server.port=8765
      
          # Start: Eureka client config
      
          # Map of availability zone to list of fully qualified URLs to communicate with eureka server. 
          # Each value can be a single URL or a comma separated list of alternative locations. 
          # Typically the eureka server URLs carry protocol,host,port,context and version information if any. 
          # Example: https://ec2-256-156-243-129.compute-1.amazonaws.com:7001/eureka/ 
          eureka.client.service-url.defaultZone=http://localhost:8761/eureka
      
          # Enables the Eureka health check handler.
          eureka.client.healthcheck.enabled=true
      
          eureka.instance.lease-renewal-interval-in-seconds=60
          eureka.instance.lease-expiration-duration-in-seconds=60
      
          # End: Eureka client config
      
      
          # Start: Api Gateway
      
          # Flag that enables Discovery Client gateway integration.
          # This allows us to invoke service using service-name registered in Eureka
          # http://localhost:8765/B3-CURRENCY-EXCHANGE-SERVICE/currency-exchange/from/usd/to/inr
          # Disbling - to use Routes and Filters
          #spring.cloud.gateway.discovery.locator.enabled=true
          spring.cloud.gateway.discovery.locator.enabled=false
      
          # Option to lower case serviceId in predicates and filters, defaults to false. 
          # Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/**
          # Disbling - to use Routes and Filters
          #spring.cloud.gateway.discovery.locator.lower-case-service-id=true
          spring.cloud.gateway.discovery.locator.lower-case-service-id=false
      
          # Option to lower case serviceId in predicates and filters, defaults to false. 
          # Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/**
          # spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
      
          # End: Api Gateway	
      
      

Note: When using routes in API Gateway, eureka discover client must be disabled in application.properties.


10. API Gateway : Global filter

Project ref: b8-api-gateway-routes

  • Purpose / Feature
    • Contract for interception-style, chained processing of gateway requests that may be used to implement cross-cutting, application-agnostic requirements such as security, timeouts, and others
    • Only applies to matched gateway routes. Copied from framework WebFilter.
  • Steps
    • Step-1: Considering tht API Gateway is already implemented with working routes config
      • If not, refer section 8 & section 9 above for API Gateway coniguration.
    • Step-2: Create a class implementing o.s.c.g.filter.GlobalFilter class.
    • Step-3: Override filter(....) method and add the business logic.
  • Maven / External dependency
    • Required dependency. ```xml org.springframework.cloudspring-cloud-starter-gatewayorg.springframework.cloudspring-cloud-starter-netflix-eureka-client
  • Code / Config changes
    • Configuration:LoggingGlobalFilter.java
      • imports
        • import some.dependent.resource
        • import org.springframework.cloud.gateway.filter.GatewayFilterChain;
        • import org.springframework.cloud.gateway.filter.GlobalFilter;
        • import org.springframework.web.server.ServerWebExchange;
        • import reactor.core.publisher.Mono;
      • Implement o.s.c.g.filter.GlobalFilter class and filter(...) method.
          @Component
          public class LoggingGlobalFilter implements GlobalFilter {
        
              private static Logger logger = LoggerFactory.getLogger(LoggingGlobalFilter.class);
        
              @Override
              public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        
                  // log the request
                  logger.info("Request -> Method: {}, Path: {}", exchange.getRequest().getMethod(),
                          exchange.getRequest().getPath());
        
                  return chain.filter(exchange);
              }
          }