Spring Cloud Gateway WebFilter Factories – Spring Cloud Gateway WebFilter Factories

最后修改: 2020年 5月 9日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Spring Cloud Gateway is an intelligent proxy service often used in microservices. It transparently centralizes requests in a single entry point and routes them to the proper service. One of its most interesting features is the concept of filters (WebFilter or GatewayFilter).

Spring Cloud Gateway是一种智能代理服务,通常在微服务中使用。它以透明的方式将请求集中在一个单一的入口点,并将它们路由到适当的服务。其最有趣的功能之一是过滤器的概念(WebFilterGatewayFilter)。

WebFilter, together with Predicate factories, incorporate the complete routing mechanism. Spring Cloud Gateway provides many built-in WebFilter factories that allow interacting with the HTTP requests before reaching the proxied service and the HTTP responses before delivering the result to the client. It is also possible to implement custom filters.

WebFilterPredicate工厂一起,整合了完整的路由机制。Spring Cloud Gateway提供了许多内置的WebFilter工厂,允许在到达代理服务之前与HTTP请求交互,并在将结果交付给客户端之前与HTTP响应交互。也可以实现自定义过滤器

In this tutorial, we’ll focus on the built-in WebFilter factories included in the project and how to use them in advanced use cases.

在本教程中,我们将重点介绍项目中包含的内置WebFilter工厂以及如何在高级用例中使用它们。

2. WebFilter Factories

2.WebFilter工厂

WebFilter (or GatewayFilter) factories allow modifying the inbound HTTP requests and outbound HTTP responses. In this sense, it offers a set of interesting functionalities to apply before and after interacting with the downstream services.

WebFilter(或GatewayFilter)工厂允许修改入境的HTTP请求和出境的HTTP响应。在这个意义上,它提供了一套有趣的功能,可以在与下游服务交互之前和之后应用。

Spring Cloud Gateway WebFilter Factories Architecture

The Handler Mapping manages the client’s request. It checks whether it matches some configured route. Then, it sends the request to the Web Handler to execute the specific filter chain for this route. The dotted line splits the logic between pre- and post-filter logic. The income filters run before the proxy request. The output filters enter into action when they receive the proxy response. Filters provide mechanisms to modify the process in between.

处理程序映射管理着客户的请求。它检查它是否与某些配置的路由相匹配。然后,它将请求发送到网络处理程序,以执行该路由的特定过滤链。虚线将逻辑分割成前过滤器和后过滤器逻辑。收入过滤器在代理请求之前运行。输出过滤器在收到代理响应时进入行动。过滤器提供机制来修改中间的过程。

3. Implementing WebFilter Factories

3.实现WebFilter工厂

Let’s review the most important WebFilter factories incorporated in the Spring Cloud Gateway project. There are two ways to implement them, using YAML or Java DSL. We’ll show examples of how to implement both.

让我们回顾一下Spring Cloud Gateway项目中最重要的WebFilter工厂。有两种方法来实现它们,使用YAML或Java DSL。我们将展示如何实现这两种方法的例子。

3.1. HTTP Request

3.1 HTTP请求

The built-in WebFilter factories allow interacting with the headers and parameters of the HTTP request. We can add (AddRequestHeader), map (MapRequestHeader), set or replace (SetRequestHeader), and remove (RemoveRequestHeader) header values and send them to the proxied service. The original host header can also be kept (PreserveHostHeader).

内置的WebFilter工厂允许与HTTP请求的头文件和参数进行交互。我们可以添加 (AddRequestHeader), 映射 (MapRequestHeader),设置或替换 (SetRequestHeader)移除(RemoveRequestHeader)头值并将其发送到代理服务。原始的主机头也可以被保留(PreserveHostHeader)。

In the same way, we can add (AddRequestParameter) and remove (RemoveRequestParameter) parameters to be processed by the downstream service. Let’s see how to do it:

以同样的方式,我们可以添加(AddRequestParameter)移除(RemoveRequestParameter)参数来被下游服务处理。让我们看看如何做到这一点。

- id: add_request_header_route
  uri: https://httpbin.org
  predicates:
  - Path=/get/**
  filters:
  - AddRequestHeader=My-Header-Good,Good
  - AddRequestHeader=My-Header-Remove,Remove
  - AddRequestParameter=var, good
  - AddRequestParameter=var2, remove
  - MapRequestHeader=My-Header-Good, My-Header-Bad
  - MapRequestHeader=My-Header-Set, My-Header-Bad
  - SetRequestHeader=My-Header-Set, Set 
  - RemoveRequestHeader=My-Header-Remove
  - RemoveRequestParameter=var2

Let’s check if everything works as expected. For that, we’ll use curl and the publicly available httpbin.org:

让我们检查一下一切是否如预期的那样工作。为此,我们将使用curl和公开可用的httpbin.org

$ curl http://localhost:8080/get
{
  "args": {
    "var": "good"
  },
  "headers": {
    "Host": "localhost",
    "My-Header-Bad": "Good",
    "My-Header-Good": "Good",
    "My-Header-Set": "Set",
  },
  "origin": "127.0.0.1, 90.171.125.86",
  "url": "https://localhost:8080/get?var=good"
}

We can see the curl response as a consequence of the request filters configured. They add My-Header-Good with value Good and map its content to My-Header-Bad. They remove My-Header-Remove and set a new value to My-Header-Set. In the args and url sections, we can see a new parameter var added. Furthermore, the last filter removes the var2 parameter.

我们可以看到curl响应是配置了请求过滤器的结果。他们添加了My-Header-Good,其值为Good,并将其内容映射到My-Header-Bad.,他们删除了My-Header-Remove,并为My-Header-Set设置了新值。在argsurl部分,我们可以看到添加了一个新的参数var。此外,最后一个过滤器删除了var2参数。

In addition, we can modify the request body before reaching the proxied service. This filter can only be configured using the Java DSL notation. The snippet below just uppercases the content of the response body:

此外,我们可以在到达代理服务之前修改请求正文。这个过滤器只能使用Java DSL符号进行配置。下面的片段只是将响应体的内容大写。

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
     return builder.routes()
       .route("modify_request_body", r -> r.path("/post/**")
         .filters(f -> f.modifyRequestBody(
           String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, 
           (exchange, s) -> Mono.just(new Hello(s.toUpperCase()))))
         .uri("https://httpbin.org"))
       .build();
}

To test the snippet, let’s execute curl with the -d option to include the body “Content”:

为了测试这个片段,让我们用-d选项执行curl,以包括主体“Content”

$ curl -X POST "http://localhost:8080/post" -i -d "Content"
"data": "{\"message\":\"CONTENT\"}",
"json": {
    "message": "CONTENT"
}

We can see that the content of the body is now uppercased to CONTENT as a result of the filter.

我们可以看到,由于过滤器的作用,正文的内容现在被大写成了CONTENT

3.2. HTTP Response

3.2 HTTP响应

Likewise, we can modify response headers by using add (AddResponseHeader), set or replace (SetResponseHeader), remove (RemoveResponseHeader) and rewrite (RewriteResponseHeader). Another functionality over the response is to dedupe (DedupeResponseHeader) to overwrite strategies and avoid duplication on them. We can get rid of backend-specific details regarding version, location, and host by using another built-in factory (RemoveLocationResponseHeader).

同样,我们可以通过使用添加AddResponseHeader)、设置或替换(SetResponseHeader)来修改响应头信息。spring.io/spring-cloud-gateway/reference/html/#removeresponseheader-gatewayfilter-factory”>remove(RemoveResponseHeader)和rewriteRewriteResponseHeader)。响应之上的另一个功能是dedupeDedupeResponseHeader),以覆盖策略并避免对其重复。我们可以通过使用另一个内置工厂(RemoveLocationResponseHeader)来摆脱关于版本、位置和主机的特定后端细节。

Let’s see a complete example:

让我们看看一个完整的例子。

- id: response_header_route
  uri: https://httpbin.org
  predicates:
  - Path=/header/post/**
  filters:
  - AddResponseHeader=My-Header-Good,Good
  - AddResponseHeader=My-Header-Set,Good
  - AddResponseHeader=My-Header-Rewrite, password=12345678
  - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
  - AddResponseHeader=My-Header-Remove,Remove
  - SetResponseHeader=My-Header-Set, Set
  - RemoveResponseHeader=My-Header-Remove
  - RewriteResponseHeader=My-Header-Rewrite, password=[^&]+, password=***
  - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,

Let’s use curl to display the response headers:

让我们用curl来显示响应头信息。

$ curl -X POST "http://localhost:8080/header/post" -s -o /dev/null -D -
HTTP/1.1 200 OK
My-Header-Good: Good
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
My-Header-Rewrite: password=***
My-Header-Set: Set

Similarly to the HTTP request, we can modify the response body. For this example, we overwrite the body of the PUT response:

与HTTP请求类似,我们可以修改响应体。在这个例子中,我们覆盖了PUT响应的主体。

@Bean
public RouteLocator responseRoutes(RouteLocatorBuilder builder) {
    return builder.routes()
      .route("modify_response_body", r -> r.path("/put/**")
        .filters(f -> f.modifyResponseBody(
          String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, 
          (exchange, s) -> Mono.just(new Hello("New Body"))))
        .uri("https://httpbin.org"))
      .build();
}

Let’s use the PUT endpoint to test the functionality:

让我们使用PUT端点来测试该功能。

$ curl -X PUT "http://localhost:8080/put" -i -d "CONTENT"
{"message":"New Body"}

3.3. Path

3.3. 路径

One of the features provided with the built-in WebFilter factories is the interaction with the paths configured by the client. It is possible to set a different path (SetPath), rewrite (RewritePath), add a prefix (PrefixPath), and strip (StripPrefix) to extract only parts of it. Remember that the filters are executed in order based on their positions in the YAML file. Let’s see how to configure the routes:

内置WebFilter工厂提供的功能之一是与客户端配置的路径的互动。可以设置不同的路径SetPath),重写RewritePath),添加前缀PrefixPath),以及片段StripPrefix)以仅提取其中部分。请记住,过滤器是根据它们在YAML文件中的位置按顺序执行的。让我们来看看如何配置路由。

- id: path_route
  uri: https://httpbin.org
  predicates:
  - Path=/new/post/**
  filters:
  - RewritePath=/new(?<segment>/?.*), $\{segment}
  - SetPath=/post

Both filters remove the subpath /new before reaching the proxied service. Let’s execute curl:

这两个过滤器在到达代理服务之前都删除了子路径/new。让我们执行curl。

$ curl -X POST "http://localhost:8080/new/post" -i
"X-Forwarded-Prefix": "/new"
"url": "https://localhost:8080/post"

We could also use the StripPrefix factory. With StripPrefix=1, we can get rid of the first subpath when contacting the downstream service.

我们还可以使用StripPrefix工厂。通过StripPrefix=1,我们可以在联系下游服务时摆脱第一个子路径。

3.4. Related to HTTP Status

3.4.与HTTP状态有关

RedirectTo takes two parameters: status and URL. The status must be a series of 300 redirection HTTP code and the URL a valid one. SetStatus takes one parameter status that can be an HTTP code or its string representation. Let’s have a look at a couple of examples:

RedirectTo需要两个参数:状态和URL。status必须是一系列300重定向的HTTP代码,URL必须是有效的。SetStatus 接收一个参数status,可以是HTTP代码或其字符串表示。让我们看一下几个例子。

- id: redirect_route
  uri: https://httpbin.org
  predicates:
  - Path=/fake/post/**
  filters:
  - RedirectTo=302, https://httpbin.org
- id: status_route
  uri: https://httpbin.org
  predicates:
  - Path=/delete/**
  filters:
  - SetStatus=401

The first filter acts over the /fake/post path, and the client is redirected to https://httpbin.org with an HTTP status 302:

第一个过滤器作用于/fake/post路径,客户端被重定向到https://httpbin.org,HTTP状态302

$ curl -X POST "http://localhost:8080/fake/post" -i
HTTP/1.1 302 Found
Location: https://httpbin.org

The second filter detects the /delete path, and an HTTP status 401 is set:

第二个过滤器检测到/delete路径,并设置HTTP状态401

$ curl -X DELETE "http://localhost:8080/delete" -i
HTTP/1.1 401 Unauthorized

3.5. Request Size Limit

3.5.请求大小限制

Finally, we can restrict the size limit of the request (RequestSize). If the request size is beyond the limit, the gateway rejects access to the service:

最后,我们可以限制请求的大小限制RequestSize)。如果请求的大小超过了限制,网关将拒绝对服务的访问

- id: size_route
  uri: https://httpbin.org
  predicates:
  - Path=/anything
  filters:
  - name: RequestSize
    args:
       maxSize: 5000000

4. Advanced Use Cases

4.高级用例

Spring Cloud Gateway offers other advanced WebFilter factories to support baseline functionalities for the microservices pattern.

Spring Cloud Gateway提供了其他高级WebFilter工厂,以支持微服务模式的基本功能。

4.1. Circuit Breaker

4.1.断路器

Spring Cloud Gateway has a built-in WebFilter factory for Circuit Breaker capability. The factory permits different fallback strategies and Java DSL route configuration. Let’s see a simple example:

Spring Cloud Gateway有一个内置的WebFilter工厂,用于Circuit Breaker功能。该工厂允许不同的回退策略和Java DSL路由配置。让我们看一个简单的例子。

- id: circuitbreaker_route
  uri: https://httpbin.org
  predicates:
  - Path=/status/504
  filters:
  - name: CircuitBreaker
  args:
     name: myCircuitBreaker
     fallbackUri: forward:/anything
  - RewritePath=/status/504, /anything

For the configuration of the Circuit Breaker, we used Resilience4J by adding the spring-cloud-starter-circuitbreaker-reactor-resilience4j dependency:

对于断路器的配置,我们通过添加Resilience4J依赖关系,使用spring-cloud-starter-circuitbreaker-reactor-resilience4j

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

Again, we can test the functionality using curl:

同样,我们可以用curl来测试这个功能。

$ curl http://localhost:8080/status/504 
"url": "https://localhost:8080/anything"

4.2. Retry

4.2 重试

Another advanced feature allows the client to retry access when something happens with proxied services. It takes several parameters, such as the number of retries, the HTTP status codes (statuses) and methods that should be retried, series, exceptions, and backoff intervals to wait after each retry. Let’s look at the YAML configuration:

另一个高级功能允许客户端在代理服务出现问题时重试访问。它需要几个参数,比如重试的次数、应该重试的HTTP状态码(statuses)和methodsseriesexceptions以及每次重试后等待的backoff间隔。让我们来看看YAML配置。

- id: retry_test
  uri: https://httpbin.org
  predicates:
  - Path=/status/502
  filters:
  - name: Retry
    args:
       retries: 3
       statuses: BAD_GATEWAY
       methods: GET,POST
       backoff:
          firstBackoff: 10ms
          maxBackoff: 50ms
          factor: 2
          basedOnPreviousValue: false

When the client reaches /status/502 (Bad Gateway), the filter retries three times, waiting for the backoff intervals configured after each execution. Let’s see how it works:

当客户端到达/status/502 (Bad Gateway)时,过滤器重试三次,等待每次执行后配置的退避间隔。让我们看看它是如何工作的。

$ curl http://localhost:8080/status/502

At the same time, we need to check the Gateway logs in the server:

同时,我们需要检查服务器中的网关日志。

Mapping [Exchange: GET http://localhost:8080/status/502] to Route{id='retry_test', ...}
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet

The filter retries three times with this backoff for methods GET and POST when the gateway receives status 502.

当网关收到状态502时,过滤器对GET和POST方法重试三次,并采用这种后退法。

4.3. Save Session and Secure Headers

4.3.保存会话和安全头文件

The SecureHeader factory adds HTTP security headers to the response. Similarly, SaveSession is of particular importance when used with Spring Session and Spring Security:

SecureHeader工厂在响应中添加HTTP安全头信息。同样,SaveSession在与Spring SessionSpring Security一起使用时也特别重要。

filters: 
- SaveSession

This filter stores the session state before making the forwarded call.

这个过滤器在进行转发呼叫前存储会话状态

4.4. Request Rate Limiter

4.4.请求速率限制器

Last but not least, the RequestRateLimiter factory determines if the request can proceed.  If not, it returns an HTTP code status 429 – Too Many Requests. It uses different parameters and resolvers to specify the rate limiter.

最后,RequestRateLimiter工厂确定请求是否可以继续。 如果不能,它将返回HTTP代码状态429 – Too Many Requests它使用不同的参数和解析器来指定速率限制器

The RedisRateLimiter uses the well-known Redis database to check the number of tokens the bucket can keep. It requires the following dependency:

RedisRateLimiter使用著名的Redis数据库来检查桶可以保存的令牌数量。它需要以下依赖性。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
 </dependency>

Consequently, it also needs the configuration of Spring Redis:

因此,它也需要配置Spring Redis

spring:
  redis:
    host: localhost
    port: 6379

The filter has several properties. The first argument, replenishRate, is the number of requests per second allowed. The second argument, burstCapacity, is the maximum number of requests in a single second. The third parameter, requestedTokens, is how many tokens the request costs. Let’s see an example implementation:

该过滤器有几个属性。第一个参数,replenishRate,是允许每秒的请求数。第二个参数,burstCapacity,是一秒钟内的最大请求数。第三个参数,requestedTokens,是请求需要花费多少代币。让我们看看一个实现的例子。

- id: request_rate_limiter
  uri: https://httpbin.org
  predicates:
  - Path=/redis/get/**
  filters:
  - StripPrefix=1
  - name: RequestRateLimiter
    args:
       redis-rate-limiter.replenishRate: 10
       redis-rate-limiter.burstCapacity: 5

Let’s use curl to test the filter. Beforehand, remember to start a Redis instance, for example using Docker:

让我们用curl来测试一下这个过滤器。在此之前,记得启动一个Redis实例,例如使用Docker

$ curl "http://localhost:8080/redis/get" -i
HTTP/1.1 200 OK
X-RateLimit-Remaining: 4
X-RateLimit-Requested-Tokens: 1
X-RateLimit-Burst-Capacity: 5
X-RateLimit-Replenish-Rate: 10

Once the remaining rate limit reaches zero, the gateway raises HTTP code 429. For testing the behavior, we can use the unit tests. We start an Embedded Redis Server and run RepeatedTests in parallel. Once the bucket reaches the limit, the error begins to display:

一旦剩余的速率限制达到零,网关会引发HTTP代码429。为了测试该行为,我们可以使用单元测试。我们启动一个Embedded Redis服务器并并行运行RepeatedTests。一旦桶达到极限,就开始显示错误。

00:57:48.263 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[4]
00:57:48.394 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[3]
00:57:48.530 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[2]
00:57:48.667 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[1]
00:57:48.826 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[0]
00:57:48.851 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->429, reason->Too Many Requests, remaining->[0]
00:57:48.894 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->429, reason->Too Many Requests, remaining->[0]
00:57:49.135 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[4]

5. Conclusion

5.总结

In this tutorial, we covered Spring Cloud Gateway’s WebFilter factories. We showed how to interact with the requests and responses from the client before and after executing the proxied service.

在本教程中,我们介绍了Spring Cloud Gateway的WebFilter工厂。我们展示了在执行代理服务之前和之后如何与来自客户端的请求和响应进行交互。

As always, the code is available over on GitHub.

像往常一样,代码可在GitHub上获得