Spring Cloud Gateway Custom Filter

Reading Time: 4 minutes

Spring Cloud Gateway

Spring Cloud Gateway provides a library for building API gateways on top of Spring and Java. It provides a flexible way of routing requests based on a number of criteria, as well as focuses on cross-cutting concerns such as security, resiliency, and monitoring. An API gateway allows you to implement the complexity separately from the client, moving that responsibility from user side to server side. All the client needs to know is how to talk to the gateway. It doesn’t matter whether the backend services run, go offline, or become unstable as long as the gateway knows how to handle these situations.

Spring Cloud Gateway Architecture

  • Route: Route is the basic building block of the gateway. It consists of
    • ID
    • destination URI
    • Collection of predicates and a collection of filters
  • A Route is matched if the aggregate’s predicate is true.
  • Predicate: It is similar to Java 8 Function Predicate. Using this functionality we can match HTTP request, such as headers , url, cookies or parameters.
  • Filter: These are instances of Spring Framework GatewayFilter. Using this we can modify the request or response as per the requirement.

Implement Student Microservice

Maven project will be like this

student-service-architecture

The pom.xml will be as follows-

<?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 http://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>2.1.7.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javainuse</groupId>
	<artifactId>student-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>  
		    <groupId>com.googlecode.json-simple</groupId>  
		    <artifactId>json-simple</artifactId>  
		    <version>1.1</version>  
		</dependency>
	</dependencies>

</project>

Define the application.yml as follows

spring:
  application:
    name: student-service
server:
  port: 8081

Create the bootstrap class(StudentApplication) with the @SpringBootApplication annotation

package com.springmicroservice;

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

@SpringBootApplication
public class StudentApplication {

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

}

Create a Controller class that exposes the GET REST service as follows

package com.springmicroservice.controller;

import org.json.simple.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/student")
public class StudentController {

	@GetMapping("/records")
	public JSONObject test() {
		JSONObject obj=new JSONObject();  
		  obj.put("subject_code","Math");   
		  obj.put("dept_name","CSE"); 
		  obj.put("name","Sumit");  
		  obj.put("id",new Integer(101));  
		  System.out.println("Student Service Invoked...");
	return obj;
	}
}

Implement Subject Microservice

Maven project will be like this

subject-service-architecture

The pom.xml will be as follows-

<?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 http://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>2.1.7.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javainuse</groupId>
	<artifactId>subject-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>  
		    <groupId>com.googlecode.json-simple</groupId>  
		    <artifactId>json-simple</artifactId>  
		    <version>1.1</version>  
		</dependency>
	</dependencies>

</project>

Define the application.properties as follows

spring:
application:
name: subject-service
server:
port: 8082

Create the bootstrap class(SubjectApplication) with the @SpringBootApplication annotation

package com.springmicroservice;

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

@SpringBootApplication
public class SubjectApplication {

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

}

Create a Controller class that exposes the GET REST service as follows-

package com.springmicroservice.controller;

import org.json.simple.JSONObject;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/subject")
public class SubjectController {

	@GetMapping("/records")
	public JSONObject test() {
		JSONObject obj=new JSONObject();   
		  obj.put("code","Math");   
		  obj.put("name","Mathematics");  
		  obj.put("subjectId",new Integer(101));  
		  System.out.println("Subject Service Invoked...");
	return obj;
	}
}

Implement Spring Cloud Gateway using property based config

cloud-gateway-service-architecture

The pom.xml will be as follows

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javainuse</groupId>
	<artifactId>cloud-gateway-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gateway-service</name>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</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>
	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
		</repository>
	</repositories>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Create the bootstrap class(APIGatewayApplication) with the @SpringBootApplication annotation

package com.springcloudgateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;

import reactor.core.publisher.Mono;

@SpringBootApplication
public class APIGatewayApplication {

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

To make Custom filters we need to inherit AbstractGatewayFilterFactory class.

package com.springcloudgateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

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) -> {
	System.out.println("First pre filter" + exchange.getRequest());
        //Custom Post Filter.Suppose we can call error response handler based on error code.
	return chain.filter(exchange).then(Mono.fromRunnable(() -> {
				System.out.println("First post filter");
		    }));
	    };
    }

    public static class Config {
	 // Put the configuration properties
    }
}

Specify the Custom Filter in the route in application.yml file

server:
  port: 8080

spring:
  cloud:
    gateway:
      routes:
      - id: studentModule
        uri: http://localhost:8081/
        predicates:
        - Path=/student/**
        filters:
        - CustomFilter
      - id: subjectModule
        uri: http://localhost:8082/
        predicates:
        - Path=/subject/**
        filters:
        - CustomFilter

Start the applications-

  • cloud-gateway-service
  • student-service
  • subject-service

Run the application.

  • Go to browser and type localhost:8080/student/records –

spring-cloud-gateway

In the browser we can see that the post filter has been applied to response.

student-service

Go to browser and type localhost:8080/subject/records

spring-cloud-gateway

In the browser we can see that the post filter has been applied to response.

subject-service

Conclusion

In this blog, we have covered how to implement and configure Custom Filter to Spring Cloud Gateway Service. Now you are ready to go to implement Custom Filter to Spring Cloud gateway. For more, you can refer to the documentation: https://cloud.spring.io/spring-cloud-gateway/reference/html/

Written by 

Gaurav Dubey is a Java Developer at Knoldus Software LLP. He has done M.CA from Kurukshetra University and completed Bachelor's Degree in Computer Science from M. D. University. He is a tech enthusiast with good knowledge of Java. He is majorly focused in Java practice. On the personal front, he loves to cook and travel.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading