Spring Cloud Alibaba: Gateway's routing filter factory

In the previous blog, the blogger introduced some of the Gateway's routing filter factories:

Construction works

One parent module and two child modules (server module provides services and gateway module implements gateway).

POM of parent module xml:

<?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.kaven</groupId>
    <artifactId>alibaba</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <description>Spring Cloud Alibaba</description>
    <modules>
        <module>server</module>
        <module>gateway</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-cloud-version>Hoxton.SR9</spring-cloud-version>
        <spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

server module

pom.xml:

<?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">
    <parent>
        <groupId>com.kaven</groupId>
        <artifactId>alibaba</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml:

server:
  port: 8085

Interface definition:

package com.kaven.alibaba.controller;

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

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@RestController
public class MessageController {

    @GetMapping("/message")
    public String getMessage(HttpServletRequest httpServletRequest) {
        StringBuilder result = new StringBuilder("hello kaven, this is spring cloud alibaba\n");
        result.append(getKeyAndValue(httpServletRequest));
        return result.toString();
    }

    // Get the StringBuilder composed of key and value in the header
    private StringBuilder getKeyAndValue(HttpServletRequest httpServletRequest) {
        StringBuilder result = new StringBuilder();
        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = httpServletRequest.getHeader(key);
            result.append(key).append(" : ").append(value).append("\n");
        }
        return result;
    }
}

Startup class:

package com.kaven.alibaba;

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

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

gateway module

pom.xml:

<?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">
    <parent>
        <groupId>com.kaven</groupId>
        <artifactId>alibaba</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

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

application.yml:

server:
  port: 8086

spring:
  cloud:
    gateway:
      routes:
        - id: server
          uri: http://localhost:8085
          predicates:
            - Path=/message

Startup class:

package com.kaven.alibaba;

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

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

Start two applications and access them with Postman http://localhost:8086/message The results are shown in the figure below:

It indicates that both server module and gateway module have been successfully applied.

RemoveRequestHeader

The RemoveRequestHeader routing filter factory accepts a name parameter, that is, the name of the request header to be deleted.

Relevant parts of source code:

Add configuration of routing filter:

          filters:
            - RemoveRequestHeader=Username


If you need to delete multiple request headers, you can specify them as follows:

          filters:
            - RemoveRequestHeader=Username
            - RemoveRequestHeader=Password

RemoveResponseHeader

The RemoveResponseHeader routing filter factory accepts a name parameter, that is, the name of the response header to be deleted.

Relevant parts of source code:

The undeleted response header is shown in the following figure:

Modify the configuration of routing filter:

          filters:
            - RemoveResponseHeader=Content-Type
            - RemoveResponseHeader=Date

RewritePath

The RewritePath routing filter factory accepts a path regexp parameter and a replacement parameter, and uses Java regular expressions to flexibly rewrite the request path.

Relevant parts of source code:

Modify the configuration of routing filter:

          predicates:
            - Path=/**
          filters:
            - RewritePath=/(?<segment>.*), /message

This configuration means that no matter what request path is, it will be rewritten as / message to access http://localhost:8085/message .


RewriteResponseHeader

The RewriteResponseHeader routing filter factory requires name, regexp and replacement parameters, and uses Java regular expressions to flexibly rewrite the response header value.

Relevant parts of source code:

It can be seen from the source code that when there is no response header with this name, the routing filter will not work.

Modify the interface in the server module:

    @GetMapping("/message")
    public String getMessage(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        StringBuilder result = new StringBuilder("hello kaven, this is spring cloud alibaba\n");
        result.append(getKeyAndValue(httpServletRequest));
        httpServletResponse.setHeader("Password", "123456");
        return result.toString();
    }


Modify the configuration of routing filter:

          predicates:
            - Path=/message
          filters:
            - RewriteResponseHeader=Password,[^&]+,******

The password in the response header is set to invisible.

SetPath

The SetPath routing filter factory adopts the path template parameter to provide a simple method to operate the request path by allowing the templated segments of the path. It uses the uri template in the Spring Framework to allow multiple matching segments.

Relevant parts of source code:

Add two new interfaces to the server module:

    @GetMapping("/message/new")
    public String getMessageNew() {
        return "/message/new";
    }

    @GetMapping("/data/new")
    public String getDataNew() {
        return "/data/new";
    }

Modify the configuration of routing filter:

          predicates:
            - Path=/{segment}
          filters:
            - SetPath=/{segment}/new

visit http://localhost:8086/message , will be routed to http://localhost:8085/message/new .

Similarly, visit http://localhost:8086/data , will be routed to http://localhost:8085/data/new .

Multiple templated segments (the following configuration ignores the second templated segment):

          predicates:
            - Path=/{segment1}/{segment2}
          filters:
            - SetPath=/{segment1}/new

SetResponseHeader

The SetResponseHeader routing filter factory requires name and value parameters. This routing filter replaces the specified response header with the given name and value parameters instead of adding (if there is no response header with the given name, this response header will be added through the name and value parameters).

Relevant parts of source code:

Modify the configuration of routing filter:

          predicates:
            - Path=/message
          filters:
            - SetResponseHeader=Password, ******

The service is requested directly instead of the gateway. The response header is shown in the following figure:

Request gateway, the specified response header will be replaced.

Modify the interface (comment out the part of the interface setting Password response header):

    @GetMapping("/message")
    public String getMessage(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        StringBuilder result = new StringBuilder("hello kaven, this is spring cloud alibaba\n");
        result.append(getKeyAndValue(httpServletRequest));
//        httpServletResponse.setHeader("Password", "123456");
        return result.toString();
    }

When there is no response header with a given name (Password response header is not added to the response of downstream services), this response header will be added through the name and value parameters

SetResponseHeader and RewriteResponseHeader are different routing filters. If there is a response header with a given name, the former is completely replaced, The latter is based on whether the regular expression matches (all matching strings are replaced); if there is no response header with a given name, the former will add the response header through the given parameters, while the latter will not.

SetStatus

The SetStatus routing filter factory accepts a single status parameter. It must be a valid Spring HttpStatus, which can be an integer value 404 of an enumerated instance or a string representation NOT_FOUND.

Relevant parts of source code:

It can be seen from the source code that the status code of the response will be modified. If there is a response header representing the status code, it also needs to be modified.

Some Spring HttpStatus enumeration instances:

    BAD_REQUEST(400, "Bad Request"),
    UNAUTHORIZED(401, "Unauthorized"),
    PAYMENT_REQUIRED(402, "Payment Required"),
    FORBIDDEN(403, "Forbidden"),
    NOT_FOUND(404, "Not Found"),
    METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
    NOT_ACCEPTABLE(406, "Not Acceptable"),

Modify the configuration of routing filter:

          predicates:
            - Path=/message
          filters:
            - SetStatus=405

Only the status code of the response is set, and the information in the response body will not be modified.

It indicates that the request is successful, but the status code of the response is modified by the gateway. The Debug is shown in the following figure:

Modify the configuration of routing filter:

          predicates:
            - Path=/message
          filters:
            - SetStatus=Method_Not_Allowed


Debug is shown in the following figure:

Other routing filter factories will be introduced in future blogs. If the blogger has something wrong or you have different opinions, you are welcome to comment and supplement.

Keywords: Java Spring Cloud gateway

Added by mhouldridge on Thu, 30 Dec 2021 19:11:37 +0200