# Spring Cloud 网关

**3.1.1**

该项目提供了一个构建在 Spring 生态系统之上的 API 网关，包括： Spring 5、 Spring Boot2 和 Project Reactor。 Spring Cloud Gateway 旨在提供一种简单但有效的方法来路由到 API，并向它们提供跨领域的关注，例如：安全性、监视/度量和弹性。

## 1. How to Include Spring Cloud Gateway

要在项目中包含 Spring Cloud网关，请使用组 ID 为`org.springframework.cloud`和工件 ID 为`spring-cloud-starter-gateway`的 starter。请参阅[Spring Cloud Project page](https://projects.spring.io/spring-cloud/)，以获取有关使用当前 Spring Cloud发布系列设置构建系统的详细信息。

如果包含启动器，但不希望启用网关，请设置`spring.cloud.gateway.enabled=false`。

|   |Spring Cloud网关是建立在[Spring Boot 2.x](https://spring.io/projects/spring-boot#learn)，[Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html)，和[项目反应堆](https://projectreactor.io/docs)之上的。因此，当你使用 Spring Cloud网关时，许多你熟悉的同步库（例如 Spring 数据和 Spring 安全性）和模式可能不适用，如果你不熟悉这些项目，我们建议你在使用 Spring Cloud Gateway 之前，先阅读他们的文档，以熟悉一些新概念。|
|---| |

|   |Spring Cloud网关需要由 Spring 启动和 Spring WebFlux 提供的 Netty 运行时。它在传统 Servlet 容器中或构建为 WAR 时不工作。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## 2.词汇表

* **路线**:网关的基本构建块。它由一个 ID、一个目标 URI、一组谓词和一组筛选器定义。如果聚合谓词为 true，则匹配路由。

* **谓词**:这是[Java8 函数谓词](https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html)。输入类型是一个[ Spring Framework`ServerWebExchange`]（https://DOCS. Spring.io/ Spring/DOCS/5.0.x/javadoc-api/org/springframework/web/server/serverwebexchange.html）。这使你能够匹配 HTTP 请求中的任何内容，例如标题或参数。

* **过滤器**:这些是[`GatewayFilter`]（https://github.com/ Spring-cloud/ Spring-cloud-gateway/tree/main/ Spring-cloud-gateway-server-server/SRC/main/main/java/org/springframework/cloud/gateway/filter/geter/getter/gatewayfilter.java）的实例在这里，你可以在发送下游请求之前或之后修改请求和响应。

## 3.它是如何工作的

下图提供了 Spring Cloud网关如何工作的高级概述：

![Spring Cloud Gateway Diagram](https://docs.spring.io/spring-cloud-gateway/docs/3.1.1/reference/html/images/spring_cloud_gateway_diagram.png)

客户端向 Spring Cloud网关提出请求。如果网关处理程序映射确定请求与路由匹配，则将请求发送到网关 Web 处理程序。此处理程序通过特定于该请求的筛选链来运行该请求。用虚线划分过滤器的原因是，过滤器可以在发送代理请求之前和之后运行逻辑。所有的“pre”过滤逻辑都会被执行。然后提出代理请求。在提出代理请求之后，将运行“POST”过滤逻辑。

|   |在没有端口的路由中定义的 URI 分别获得 HTTP 和 HTTPS URI 的默认端口号 80 和 443.|
|---|----------------------------------------------------------------------------------------------------------------------|

## 4.配置路由谓词工厂和网关过滤器工厂

配置谓词和过滤器有两种方法：快捷方式和完全展开的参数。下面的大多数示例都使用了快捷方式。

名称和参数名称将以`code`的形式在每个部分的第一个或两个表示中列出。参数通常按快捷方式配置所需的顺序列出。

### 4.1.快捷方式配置

快捷方式配置由筛选器名称识别，后面跟着一个等号（`=`），后面是用逗号分隔的参数值（`,`）。

应用程序.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue
```

上一个示例用两个参数定义了`Cookie`路由谓词工厂，cookie 名`mycookie`和要匹配`mycookievalue`的值。

### 4.2.完全展开的论证

完全展开的参数看起来更像是带有名称/值对的标准 YAML 配置。通常，会有`name`键和`args`键。`args`键是用于配置谓词或筛选器的键值对的映射。

应用程序.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue
```

这是上面显示的`Cookie`谓词的快捷配置的完整配置。

## 5.路线谓词工厂

Spring Cloud网关将路由匹配为 Spring WebFlux`HandlerMapping`基础设施的一部分。 Spring Cloud网关包括许多内置的路由谓词工厂。所有这些谓词在 HTTP 请求的不同属性上匹配。你可以将多个路由谓词工厂与逻辑`and`语句组合在一起。

### 5.1.后路由谓词工厂

`After`路由谓词工厂接受一个参数，a`datetime`（这是一个 Java`ZonedDateTime`）。此谓词匹配在指定的 DateTime 之后发生的请求。下面的示例配置一个 after 路由谓词：

示例 1.应用程序.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
```

这条路线符合任何请求后提出的 JAN20,2017 年 17:42 山区时间（丹佛）。

### 5.2.前路由谓词工厂

`Before`路由谓词工厂接受一个参数，a`datetime`（这是一个 Java`ZonedDateTime`）。此谓词匹配在指定的`datetime`之前发生的请求。下面的示例配置一个 before 路由谓词：

示例 2.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
```

这条路线符合任何在 JAN20,2017 年 17:42 山区时间（丹佛）之前提出的请求。

### 5.3.路由谓词之间的工厂

`Between`路由谓词工厂接受两个参数，`datetime1`和`datetime2`，它们是 Java`ZonedDateTime`对象。此谓词匹配发生在`datetime1`之后和`datetime2`之前的请求。`datetime2`参数必须位于`datetime1`之后。下面的示例配置了一个 between 路由谓词：

示例 3.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
```

这条路线符合在 JAN20,2017 年 17:42 山区时间（丹佛）之后和 JAN21,2017 年 17:42 山区时间（丹佛）之前提出的任何请求。这对于维护窗口可能是有用的。

### 5.4.cookie 路由谓词工厂

`Cookie`路由谓词工厂接受两个参数，cookie`name`和`regexp`（这是一个 Java 正则表达式）。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。下面的示例配置了一个 Cookie 路由谓词工厂：

示例 4.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p
```

此路由匹配具有名为`chocolate`的 cookie 的请求，其值与`ch.p`正则表达式匹配。

### 5.5.头路由谓词工厂

`Header`路由谓词工厂接受两个参数，`header`和`regexp`（这是一个 Java 正则表达式）。此谓词与具有给定名称的头匹配，其值与正则表达式匹配。下面的示例配置头路由谓词：

示例 5.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+
```

如果请求有一个名为`X-Request-Id`的头，其值与`\d+`正则表达式匹配（即它有一个或多个数字的值），则此路由匹配。

### 5.6.主机路由谓词工厂

`Host`路由谓词工厂接受一个参数：主机名列表`patterns`。该模式是一种 Ant 样式的模式，以`.`作为分隔符。此谓词匹配与模式匹配的`Host`头。下面的示例配置了一个主机路由谓词：

示例 6.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org
```

URI 模板变量（如`{sub}.myhost.org`）也受到支持。

如果请求具有`Host`头，其值为`www.somehost.org`或`beta.somehost.org`或`www.anotherhost.org`，则此路由匹配。

这个谓词提取 URI 模板变量（例如`sub`，在前面的示例中定义）作为名称和值的映射，并将其放在`ServerWebExchange.getAttributes()`中，并在`ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE`中定义一个键。然后这些值可供[`GatewayFilter`工厂使用]（#gateway-route-filters）

### 5.7.路由谓词工厂的方法

`Method`路由谓词工厂接受一个`methods`参数，该参数是一个或多个参数：要匹配的 HTTP 方法。下面的示例配置了一个方法路由谓词：

示例 7.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST
```

如果请求方法是`GET`或`POST`，则此路由匹配。

### 5.8.路径谓词工厂

`Path`路由谓词工厂接受两个参数： Spring `PathMatcher``patterns`的列表和一个名为`matchTrailingSlash`的可选标志（默认为`true`）。下面的示例配置了一个路径路由谓词：

示例 8.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}
```

如果请求路径是：例如：`/red/1`或`/red/1/`或`/red/blue`或`/blue/green`，则此路由匹配。

如果`matchTrailingSlash`被设置为`false`，那么请求路径`/red/1/`将不会被匹配。

这个谓词提取 URI 模板变量（例如`segment`，在前面的示例中定义）作为名称和值的映射，并将其放在`ServerWebExchange.getAttributes()`中，并在`ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE`中定义一个键。然后这些值可供[`GatewayFilter`工厂]使用（#gateway-route-filters）

可以使用一种实用方法（称为`get`）来使对这些变量的访问更容易。下面的示例展示了如何使用`get`方法：

```
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");
```

### 5.9.查询路由谓词工厂

`Query`路由谓词工厂接受两个参数：一个必需的`param`和一个可选的`regexp`（这是一个 Java 正则表达式）。下面的示例配置一个查询路由谓词：

示例 9.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green
```

如果请求包含`green`查询参数，则前面的路由匹配。

application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.
```

如果请求包含一个`red`查询参数，其值与`gree.`regexp 匹配，则前面的路由匹配，因此`green`和`greet`将匹配。

### 5.10.RemoteAddr 路由谓词工厂

`RemoteAddr`路由谓词工厂接受一个`sources`的列表（最小大小为 1），这些字符串是 CIDR 表示法（IPv4 或 IPv6）字符串，例如`192.168.0.1/16`（其中`192.168.0.1`是一个 IP 地址，`16`是一个子网掩码）。以下示例配置了一个 RemoteAddr 路由谓词：

示例 10.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24
```

如果请求的远程地址是`192.168.1.10`，则此路由匹配。

#### 5.10.1.修改远程地址的解析方式

默认情况下，RemoteAddr 路由谓词工厂使用来自传入请求的远程地址。如果 Spring Cloud网关位于代理层的后面，这可能与实际的客户端 IP 地址不匹配。

你可以通过设置自定义`RemoteAddressResolver`来定制远程地址的解析方式。 Spring Cloud网关带有一个基于[X-forward-for 标头](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)，`XForwardedRemoteAddressResolver`的非默认远程地址解析器。

`XForwardedRemoteAddressResolver`有两种静态构造函数方法，它们采用不同的方法实现安全性：

* `XForwardedRemoteAddressResolver::trustAll`返回一个`RemoteAddressResolver`，它总是使用在`X-Forwarded-For`头中找到的第一个 IP 地址。这种方法容易受到欺骗，因为恶意客户端可能会为`X-Forwarded-For`设置初始值，该初始值将被解析器接受。

* `XForwardedRemoteAddressResolver::maxTrustedIndex`获取一个索引，该索引与在 Spring Cloud网关前运行的受信任基础设施的数量相关。 Spring 如果云网关例如只能通过 HAProxy 访问，那么应该使用 1 的值。如果在 Spring Cloud网关可访问之前需要两个值得信赖的基础设施跳，那么应该使用 2 的值。

考虑以下标头值：

```
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
```

以下`maxTrustedIndex`值产生以下远程地址：

|   `maxTrustedIndex`    |结果|
|------------------------|-----------------------------------------------------------|
|[`Integer.MIN_VALUE`,0] |（无效，`IllegalArgumentException`在初始化期间）|
|           1            |                          0.0.0.3                          |
|           2            |                          0.0.0.2                          |
|           3            |                          0.0.0.1                          |
|[4, `Integer.MAX_VALUE`]|                          0.0.0.1                          |

下面的示例展示了如何使用 Java 实现相同的配置：

例 11.gatewayconfig.java

```
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
    .maxTrustedIndex(1);

...

.route("direct-route",
    r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
        .uri("https://downstream1")
.route("proxied-route",
    r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
        .uri("https://downstream2")
)
```

### 5.11.权重路径谓词工厂

`Weight`路由谓词工厂接受两个参数：`group`和`weight`（一个 INT）。权重是按组计算的。下面的示例配置了权重路由谓词：

示例 12.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2
```

此路线将把 80% 的流量转发给[weighthigh.org](https://weighthigh.org)，并将 20% 的流量转发给[weighlow.org](https://weighlow.org)。

### 5.12.XForwarded 远程 addr 路由谓词工厂

`XForwarded Remote Addr`路由谓词工厂接受一个`sources`的列表（最小大小为 1），这些字符串是 CIDR 表示法（IPv4 或 IPv6）字符串，例如`192.168.0.1/16`（其中`192.168.0.1`是一个 IP 地址，`16`是一个子网掩码）。

此路由谓词允许基于`X-Forwarded-For`HTTP 报头对请求进行过滤。

这可以用于反向代理，例如负载均衡器或 Web 应用程序防火墙，在这些代理中，只有当请求来自由这些反向代理使用的受信任的 IP 地址列表时，才允许请求。

下面的示例配置了一个 XForWardeDremoteaddr 路由谓词：

示例 13.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: xforwarded_remoteaddr_route
        uri: https://example.org
        predicates:
        - XForwardedRemoteAddr=192.168.1.1/24
```

如果`X-Forwarded-For`标头包含`192.168.1.10`，则此路由匹配。

## 6.`GatewayFilter`工厂

路由过滤器允许以某种方式修改传入 HTTP 请求或传出 HTTP 响应。路由过滤器的作用域是特定的路由。 Spring Cloud网关包括许多内置的网关过滤工厂。

|   |有关如何使用以下任何过滤器的更详细示例，请查看[unit tests](https://github.com/spring-cloud/spring-cloud-gateway/tree/master/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 6.1.`AddRequestHeader``GatewayFilter`工厂

`AddRequestHeader``GatewayFilter`工厂接受一个`name`和`value`参数。以下示例配置`AddRequestHeader``GatewayFilter`:

示例 14.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue
```

对于所有匹配的请求，此清单将`X-Request-red:blue`头添加到下游请求的头。

`AddRequestHeader`知道用于匹配路径或主机的 URI 变量。URI 变量可以在值中使用，并在运行时展开。下面的示例配置使用变量的`AddRequestHeader``GatewayFilter`:

示例 15.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - AddRequestHeader=X-Request-Red, Blue-{segment}
```

### 6.2.`AddRequestParameter``GatewayFilter`工厂

`AddRequestParameter``GatewayFilter`工厂接受一个`name`和`value`参数。以下示例配置`AddRequestParameter``GatewayFilter`:

示例 16.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=red, blue
```

这将为所有匹配的请求将`red=blue`添加到下游请求的查询字符串中。

`AddRequestParameter`知道用于匹配路径或主机的 URI 变量。URI 变量可以在值中使用，并在运行时展开。下面的示例配置使用变量的`AddRequestParameter``GatewayFilter`:

示例 17.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - AddRequestParameter=foo, bar-{segment}
```

### 6.3.`AddResponseHeader``GatewayFilter`工厂

`AddResponseHeader``GatewayFilter`工厂接受一个`name`和`value`参数。以下示例配置`AddResponseHeader``GatewayFilter`:

示例 18.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Red, Blue
```

这将为所有匹配的请求向下游响应的头添加`X-Response-Red:Blue`头。

`AddResponseHeader`知道用于匹配路径或主机的 URI 变量。URI 变量可以在值中使用，并在运行时展开。下面的示例配置使用变量的`AddResponseHeader``GatewayFilter`:

示例 19.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - AddResponseHeader=foo, bar-{segment}
```

### 6.4.`DedupeResponseHeader``GatewayFilter`工厂

DedupeResponseHeader GatewayFilter 工厂接受一个`name`参数和一个可选的`strategy`参数。`name`可以包含一个以空格分隔的标题名称列表。下面的示例配置`DedupeResponseHeader``GatewayFilter`:

示例 20.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
```

在网关 CORS 逻辑和下游逻辑都添加响应头的情况下，这将删除重复的`Access-Control-Allow-Credentials`和`Access-Control-Allow-Origin`响应头的值。

`DedupeResponseHeader`过滤器还接受一个可选的`strategy`参数。可接受的值是`RETAIN_FIRST`（默认）、`RETAIN_LAST`和`RETAIN_UNIQUE`。

### 6.5. Spring Cloud CircuitBreaker GatewayFilter Factory

Spring Cloud断路器网关过滤器工厂使用 Spring Cloud断路器 API 将网关路由封装在断路器中。 Spring Cloud Circuitbreaker 支持可与 Spring Cloud Gateway 一起使用的多个库。 Spring Cloud支持开箱即用的弹性 4J。

要启用 Spring Cloud电路断路器过滤器，你需要在 Classpath 上放置`spring-cloud-starter-circuitbreaker-reactor-resilience4j`。以下示例配置了 Spring Cloud电路断路器`GatewayFilter`:

示例 21.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - CircuitBreaker=myCircuitBreaker
```

要配置断路器，请参阅你正在使用的底层断路器实现的配置。

* [复原力 4J 文档](https://cloud.spring.io/spring-cloud-circuitbreaker/reference/html/spring-cloud-circuitbreaker.html)

Spring Cloud电路断路器过滤器还可以接受可选的`fallbackUri`参数。目前，只支持`forward:`模式 URI。如果回退被调用，请求将被转发到与 URI 匹配的控制器。下面的示例配置了这种回退：

示例 22.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
        - RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint
```

下面的列表在 Java 中做了相同的事情：

例 23.应用程序.java

```
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}
```

当调用断路器回退时，此示例转发到`/inCaseofFailureUseThis`URI。请注意，此示例还演示了（可选的） Spring Cloud负载平衡器负载平衡（由目标 URI 上的`lb`前缀定义）。

主要的场景是使用`fallbackUri`在网关应用程序中定义内部控制器或处理程序。但是，你也可以将请求重新路由到外部应用程序中的控制器或处理程序，如下所示：

示例 24.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
```

在此示例中，网关应用程序中没有`fallback`端点或处理程序。然而，在另一个应用程序中有一个，注册在`[localhost:9994](http://localhost:9994)`下。

在请求被转发到 Fallback 的情况下， Spring Cloud Circuitbreaker 网关过滤器还提供了导致它的`Throwable`。它被添加到`ServerWebExchange`中，作为`ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR`属性，该属性可以在处理网关应用程序内的回退时使用。

对于外部控制器/处理程序场景，可以添加带有异常详细信息的头。你可以在[FallbackHeaders GatewayFilter 工厂部分](#fallback-headers)中找到有关这样做的更多信息。

#### 6.5.1.按状态码切断断路器

在某些情况下，你可能希望基于从其封装的路由返回的状态码来跳闸断路器。断路器配置对象获取一系列状态代码，如果返回这些代码，将导致断路器跳闸。在设置要跳闸的状态码时，可以使用带有状态码值的整数，也可以使用`HttpStatus`枚举的字符串表示。

示例 25.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
            statusCodes:
              - 500
              - "NOT_FOUND"
```

例 26.应用程序.java

```
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis").addStatusCode("INTERNAL_SERVER_ERROR"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}
```

### 6.6.`FallbackHeaders``GatewayFilter`工厂

`FallbackHeaders`工厂允许你在转发到外部应用程序中的`fallbackUri`的请求的标题中添加 Spring Cloud Circuitbreaker 执行异常详细信息，如下所示：

示例 27.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header
```

在此示例中，在运行断路器时发生执行异常之后，请求被转发到在`localhost:9994`上运行的应用程序中的`fallback`端点或处理程序。带有异常类型、消息和（如果可用的话）根原因异常类型和消息的头将由`FallbackHeaders`过滤器添加到该请求中。

你可以通过设置以下参数的值（以它们的默认值显示）来覆盖配置中的头的名称：

* `executionExceptionTypeHeaderName`(`"Execution-Exception-Type"`)

* `executionExceptionMessageHeaderName`(`"Execution-Exception-Message"`)

* `rootCauseExceptionTypeHeaderName`(`"Root-Cause-Exception-Type"`)

* `rootCauseExceptionMessageHeaderName`(`"Root-Cause-Exception-Message"`)

有关断路器和网关的更多信息，请参见[Spring Cloud CircuitBreaker Factory section](#spring-cloud-circuitbreaker-filter-factory)。

### 6.7.`MapRequestHeader``GatewayFilter`工厂

`MapRequestHeader``GatewayFilter`工厂接受`fromHeader`和`toHeader`参数。它创建一个新的命名头（`toHeader`），并从传入的 HTTP 请求中从现有的命名头（`fromHeader`）中提取该值。如果输入标头不存在，则过滤器不会产生任何影响。如果新的命名标头已经存在，那么它的值将被新的值扩充。以下示例配置`MapRequestHeader`:

示例 28.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: map_request_header_route
        uri: https://example.org
        filters:
        - MapRequestHeader=Blue, X-Request-Red
```

这会将`X-Request-Red:<values>`头添加到下游请求，并从传入的 HTTP 请求的`Blue`头更新其值。

### 6.8.`PrefixPath``GatewayFilter`工厂

`PrefixPath``GatewayFilter`工厂接受一个`prefix`参数。以下示例配置`PrefixPath``GatewayFilter`:

示例 29.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath
```

这将在所有匹配请求的路径前缀`/mypath`。因此，对`/hello`的请求将被发送到`/mypath/hello`。

### 6.9.`PreserveHostHeader``GatewayFilter`工厂

`PreserveHostHeader``GatewayFilter`工厂没有参数。此筛选器设置一个请求属性，路由筛选器将检查该属性以确定是否应发送原始的主机头，而不是由 HTTP 客户机确定的主机头。以下示例配置`PreserveHostHeader``GatewayFilter`:

示例 30.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader
```

### 6.10.`RequestRateLimiter``GatewayFilter`工厂

`RequestRateLimiter``GatewayFilter`工厂使用`RateLimiter`实现来确定是否允许当前请求继续进行。如果不是，则返回`HTTP 429 - Too Many Requests`（默认情况下）的状态。

该过滤器接受一个可选的`keyResolver`参数和特定于速率限制器的参数（将在本节后面描述）。

`keyResolver`是实现`KeyResolver`接口的 Bean。在配置中，使用 spel 通过名称引用 Bean。`#{@myKeyResolver}`是一个 spel 表达式，它引用名为`myKeyResolver`的 Bean。下面的清单显示了`KeyResolver`接口：

例 31.keyresolver.java

```
public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}
```

`KeyResolver`接口让可插入策略派生限制请求的键。在未来的里程碑版本中，将会有一些`KeyResolver`实现。

`KeyResolver`的默认实现是`PrincipalNameKeyResolver`，它从`ServerWebExchange`检索`Principal`并调用`Principal.getName()`。

默认情况下，如果`KeyResolver`没有找到密钥，请求将被拒绝。你可以通过设置`spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key`（`true`或`false`）和`spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code`属性来调整此行为。

|   |`RequestRateLimiter`不能使用“shortcut”符号进行配置。下面的示例是*无效*:<br/><br/>示例 32.application.properties<br/><br/>```<br/># INVALID SHORTCUT CONFIGURATION<br/>spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}<br/>```|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 6.1 0.1.Redis`RateLimiter`

Redis 实现基于在[Stripe](https://stripe.com/blog/rate-limiters)上完成的工作。它需要使用`spring-boot-starter-data-redis-reactive` Spring 引导启动器。

使用的算法是[令牌桶算法](https://en.wikipedia.org/wiki/Token_bucket)。

`redis-rate-limiter.replenishRate`属性是你希望用户在不删除任何请求的情况下每秒可以执行多少个请求。这是令牌桶被填满的速率。

`redis-rate-limiter.burstCapacity`属性是用户在一秒钟内被允许执行的最大请求数。这是令牌桶可以容纳的令牌数量。将此值设置为零将阻止所有请求。

`redis-rate-limiter.requestedTokens`属性是一个请求需要多少令牌。这是每个请求从 bucket 中获取的令牌的数量，默认为`1`。

通过在`replenishRate`和`burstCapacity`中设置相同的值，可以实现稳定的速率。可以通过将`burstCapacity`设置为高于`replenishRate`来允许临时突发。在这种情况下，速率限制器需要在两次突发之间允许一段时间（根据`replenishRate`），因为连续两次突发将导致丢弃请求（`HTTP 429 - Too Many Requests`）。下面的列表配置了`redis-rate-limiter`:

速率限制`1 request/s`通过将`replenishRate`设置为所需的请求数，`requestedTokens`设置为秒内的时间跨度，`burstCapacity`设置为`replenishRate`和`requestedTokens`的乘积，例如，设置`replenishRate=1`，`requestedTokens=60`和`burstCapacity=60`将导致`1 request/min`的限制。

示例 33.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20
            redis-rate-limiter.requestedTokens: 1
```

下面的示例在 Java 中配置一个 keyresolver:

例 34.config.java

```
@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
```

这定义了每个用户 10 个的请求速率限制。允许突发 20 个请求，但在接下来的一秒钟内，只有 10 个请求可用。`KeyResolver`是一个获得`user`请求参数的简单参数（请注意，这不推荐用于生产）。

还可以将速率限制器定义为实现`RateLimiter`接口的 Bean。在配置中，可以使用 spel 按名称引用 Bean。`#{@myRateLimiter}`是一个 spel 表达式，它引用名为`myRateLimiter`的 Bean。下面的清单定义了一个速率限制器，该限制器使用上一个清单中定义的`KeyResolver`:

示例 35.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            rate-limiter: "#{@myRateLimiter}"
            key-resolver: "#{@userKeyResolver}"
```

### 6.11.`RedirectTo``GatewayFilter`工厂

`RedirectTo``GatewayFilter`工厂接受两个参数，`status`和`url`。`status`参数应该是 300 系列重定向 HTTP 代码，例如 301.`url`参数应该是一个有效的 URL。这是`Location`标头的值。对于相对重定向，应该使用`uri: no://op`作为路由定义的 URI。下面的列表配置了`RedirectTo``GatewayFilter`:

示例 36.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - RedirectTo=302, https://acme.org
```

这将发送带有`Location:https://acme.org`头的状态 302 来执行重定向。

### 6.12.The`RemoveRequestHeader`GatewayFilter Factory

`RemoveRequestHeader``GatewayFilter`工厂接受一个`name`参数。它是要删除的标头的名称。下面的列表配置了`RemoveRequestHeader``GatewayFilter`:

示例 37.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo
```

这将在向下游发送`X-Request-Foo`头之前删除它。

### 6.13.`RemoveResponseHeader``GatewayFilter`工厂

`RemoveResponseHeader``GatewayFilter`工厂接受一个`name`参数。它是要删除的标头的名称。下面的列表配置了`RemoveResponseHeader``GatewayFilter`:

示例 38.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo
```

这将在响应返回到网关客户机之前从响应中删除`X-Response-Foo`头。

要删除任何类型的敏感报头，你应该为你可能想要删除的任何路由配置此筛选器。此外，你可以使用`spring.cloud.gateway.default-filters`配置该过滤器一次，并将其应用于所有路由。

### 6.14.`RemoveRequestParameter``GatewayFilter`工厂

`RemoveRequestParameter``GatewayFilter`工厂接受一个`name`参数。它是要删除的查询参数的名称。下面的示例配置`RemoveRequestParameter``GatewayFilter`:

示例 39.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: removerequestparameter_route
        uri: https://example.org
        filters:
        - RemoveRequestParameter=red
```

这将在向下游发送`red`参数之前删除该参数。

### 6.15.`RewritePath``GatewayFilter`工厂

`RewritePath``GatewayFilter`工厂接受一个路径`regexp`参数和一个`replacement`参数。这使用 Java 正则表达式以灵活的方式重写请求路径。下面的列表配置了`RewritePath``GatewayFilter`:

示例 40.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/red/**
        filters:
        - RewritePath=/red/?(?<segment>.*), /$\{segment}
```

对于`/red/blue`的请求路径，在发出下游请求之前，将路径设置为`/blue`。注意，由于 YAML 规范，`$`应该替换为`$\`。

### 6.16.`RewriteLocationResponseHeader``GatewayFilter`工厂

`RewriteLocationResponseHeader``GatewayFilter`工厂修改`Location`响应头的值，通常是为了去掉后台特定的细节。它需要`stripVersionMode`、`locationHeaderName`、`hostValue`和`protocolsRegex`参数。下面的列表配置了`RewriteLocationResponseHeader``GatewayFilter`:

示例 41.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: rewritelocationresponseheader_route
        uri: http://example.org
        filters:
        - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
```

例如，对于`POST [api.example.com/some/object/name](https://api.example.com/some/object/name)`的请求，`Location`的响应头值`[object-service.prod.example.net/v2/some/object/id](https://object-service.prod.example.net/v2/some/object/id)`被重写为`[api.example.com/some/object/id](https://api.example.com/some/object/id)`。

`stripVersionMode`参数具有以下可能的值：`NEVER_STRIP`、`AS_IN_REQUEST`（默认）和`ALWAYS_STRIP`。

* `NEVER_STRIP`:即使原始请求路径不包含版本，也不会剥离版本。

* `AS_IN_REQUEST`只有当原始请求路径不包含版本时，才会剥离版本。

* `ALWAYS_STRIP`即使原始请求路径包含版本，版本也总是被剥离。

如果提供了`hostValue`参数，则用于替换响应`host:port`头的`host:port`部分。如果没有提供，则使用`Host`请求头的值。

参数`protocolsRegex`必须是一个有效的 regex`String`，协议名称与该 regex 匹配。如果不匹配，过滤器就不会做任何事情。默认值为`http|https|ftp|ftps`。

### 6.17.`RewriteResponseHeader``GatewayFilter`工厂

`RewriteResponseHeader``GatewayFilter`工厂接受`name`、`regexp`和`replacement`参数。它使用 Java 正则表达式以一种灵活的方式重写响应头值。以下示例配置`RewriteResponseHeader``GatewayFilter`:

示例 42.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://example.org
        filters:
        - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
```

对于标头值`/42?user=ford&password=omg!what&flag=true`，在发出下游请求后将其设置为`/42?user=ford&password=***&flag=true`。由于 YAML 规范，你必须使用`$\`表示`$`。

### 6.18.`SaveSession``GatewayFilter`工厂

`SaveSession``GatewayFilter`工厂强制执行`WebSession::save`操作*在此之前*向下游转发呼叫。这在使用带有惰性数据存储的[Spring Session](https://projects.spring.io/spring-session/)之类的东西时特别有用，并且需要确保在进行转发调用之前保存了会话状态。以下示例配置`SaveSession``GatewayFilter`:

示例 43.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession
```

如果将[Spring Security](https://projects.spring.io/spring-security/)与 Spring 会话集成，并希望确保已将安全细节转发到远程进程，这是至关重要的。

### 6.19.`SecureHeaders``GatewayFilter`工厂

`SecureHeaders``GatewayFilter`工厂根据[这篇博文](https://blog.appcanary.com/2017/http-security-headers.html)中提出的建议，向响应添加了许多标题。

添加了以下标题（以其默认值显示）：

* `X-Xss-Protection:1 (mode=block`)

* `Strict-Transport-Security (max-age=631138519`)

* `X-Frame-Options (DENY)`

* `X-Content-Type-Options (nosniff)`

* `Referrer-Policy (no-referrer)`

* `Content-Security-Policy (default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline)'`

* `X-Download-Options (noopen)`

* `X-Permitted-Cross-Domain-Policies (none)`

要更改默认值，请在`spring.cloud.gateway.filter.secure-headers`名称空间中设置适当的属性。以下属性可供选择：

* `xss-protection-header`

* `strict-transport-security`

* `x-frame-options`

* `x-content-type-options`

* `referrer-policy`

* `content-security-policy`

* `x-download-options`

* `x-permitted-cross-domain-policies`

要禁用默认值，请使用逗号分隔的值设置`spring.cloud.gateway.filter.secure-headers.disable`属性。下面的示例展示了如何做到这一点：

```
spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security
```

|   |需要使用安全报头的小写字母全名来禁用它。|
|---|-----------------------------------------------------------------------------|

### 6.20.`SetPath``GatewayFilter`工厂

`SetPath``GatewayFilter`工厂接受一个路径`template`参数。它提供了一种简单的方法，通过允许模板化的路径段来操作请求路径。这使用了 Spring Framework 中的 URI 模板。允许多个匹配段。下面的示例配置`SetPath``GatewayFilter`:

示例 44.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - SetPath=/{segment}
```

对于`/red/blue`的请求路径，在发出下游请求之前，将路径设置为`/blue`。

### 6.21.`SetRequestHeader``GatewayFilter`工厂

`SetRequestHeader``GatewayFilter`工厂接受`name`和`value`参数。下面的列表配置了`SetRequestHeader``GatewayFilter`:

示例 45.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        filters:
        - SetRequestHeader=X-Request-Red, Blue
```

这个`GatewayFilter`用给定的名称替换（而不是添加）所有的标题。因此，如果下游服务器用`X-Request-Red:1234`响应，这将被替换为`X-Request-Red:Blue`，这是下游服务将接收的内容。

`SetRequestHeader`知道用于匹配路径或主机的 URI 变量。可以在值中使用 URI 变量，并在运行时对其进行扩展。下面的示例配置了使用变量的`SetRequestHeader``GatewayFilter`:

示例 46.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - SetRequestHeader=foo, bar-{segment}
```

### 6.22.`SetResponseHeader``GatewayFilter`工厂

`SetResponseHeader``GatewayFilter`工厂接受`name`和`value`参数。下面的列表配置了`SetResponseHeader``GatewayFilter`:

示例 47.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: https://example.org
        filters:
        - SetResponseHeader=X-Response-Red, Blue
```

这个 GatewayFilter 用给定的名称替换（而不是添加）所有的头。因此，如果下游服务器使用`X-Response-Red:1234`进行响应，则将其替换为`X-Response-Red:Blue`，这是网关客户机将接收的内容。

`SetResponseHeader`知道用于匹配路径或主机的 URI 变量。URI 变量可以在值中使用，并将在运行时展开。下面的示例配置使用变量的`SetResponseHeader``GatewayFilter`:

示例 48.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - SetResponseHeader=foo, bar-{segment}
```

### 6.23.`SetStatus``GatewayFilter`工厂

`SetStatus``GatewayFilter`工厂只接受一个参数，`status`。它必须是有效的 Spring `HttpStatus`。它可以是整数值`404`，也可以是枚举的字符串表示：`NOT_FOUND`。下面的列表配置了`SetStatus``GatewayFilter`:

示例 49.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: https://example.org
        filters:
        - SetStatus=UNAUTHORIZED
      - id: setstatusint_route
        uri: https://example.org
        filters:
        - SetStatus=401
```

在这两种情况下，响应的 HTTP 状态都设置为 401.

你可以将`SetStatus``GatewayFilter`配置为从响应中的报头中的代理请求返回原始 HTTP 状态代码。如果配置了以下属性，则会将头添加到响应中：

示例 50.application.yml

```
spring:
  cloud:
    gateway:
      set-status:
        original-status-header-name: original-http-status
```

### 6.24.`StripPrefix``GatewayFilter`工厂

`StripPrefix``GatewayFilter`工厂接受一个参数，`parts`。`parts`参数指示在向下游发送请求之前要从请求中剥离的路径中的部件数量。下面的列表配置了`StripPrefix``GatewayFilter`:

示例 51.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2
```

当通过网关向`/name/blue/red`发出请求时，向`nameservice`发出的请求看起来像`[nameservice/red](https://nameservice/red)`。

### 6.25.重试`GatewayFilter`工厂

`Retry``GatewayFilter`工厂支持以下参数：

* `retries`:应该尝试的重试次数。

* `statuses`:应该重试的 HTTP 状态代码，用`org.springframework.http.HttpStatus`表示。

* `methods`:应该重试的 HTTP 方法，用`org.springframework.http.HttpMethod`表示。

* `series`:要重试的一系列状态代码，用`org.springframework.http.HttpStatus.Series`表示。

* `exceptions`:应该重试的抛出的异常列表。

* `backoff`:为重试配置的指数退避。在回退间隔`firstBackoff * (factor ^ n)`之后执行重试，其中`n`是迭代。如果配置了`maxBackoff`，则应用的最大退避限制为`maxBackoff`。如果`basedOnPreviousValue`为真，则按`prevBackoff * factor`计算退避。

如果启用，以下默认值将配置为`Retry`过滤器：

* `retries`:三次

* `series`:5XX 系列

* `methods`:get 方法

* `exceptions`:`IOException`和`TimeoutException`

* `backoff`:禁用

下面的清单配置了重试`GatewayFilter`:

示例 52.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
            methods: GET,POST
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false
```

|   |当使用带有`forward:`前缀 URL 的重试筛选器时，目标端点应该仔细地编写，以便在发生错误的情况下，它不会执行任何可能导致将响应发送到客户机并提交的操作，例如，<br/>，如果目标端点是带注释的控制器，则目标控制器方法不应返回带有错误状态码的`ResponseEntity`，而是应该抛出<br/>或发出错误信号（例如，通过`Mono.error(ex)`返回值），重试筛选器可以配置为通过重试来处理该筛选器。|
|---| |

|   |当在带有主体的任何 HTTP 方法中使用 Retry 过滤器时，主体将被缓存，网关将成为内存约束。主体缓存在由`ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`定义的请求属性中。对象的类型是`org.springframework.core.io.buffer.DataBuffer`。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

一个简化的“shortcut”符号可以添加一个`status`和`method`。

以下两个例子是等价的：

示例 53.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: retry_route
        uri: https://example.org
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: INTERNAL_SERVER_ERROR
            methods: GET
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false

      - id: retryshortcut_route
        uri: https://example.org
        filters:
        - Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false
```

### 6.26.`RequestSize``GatewayFilter`工厂

当请求大小大于允许的限制时，`RequestSize`工厂可以限制请求到达下游服务。过滤器接受`maxSize`参数。`maxSize`是`DataSize`类型，因此值可以定义为一个数字，后面跟着一个可选的`DataUnit`后缀，例如“KB”或“MB”。对于字节，默认值为“B”。它是以字节为单位定义的请求的允许大小限制。下面的列表配置了`RequestSize``GatewayFilter`:

示例 54.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
        uri: http://localhost:8080/upload
        predicates:
        - Path=/upload
        filters:
        - name: RequestSize
          args:
            maxSize: 5000000
```

当请求由于大小而被拒绝时，`RequestSize`工厂将响应状态设置为`413 Payload Too Large`，并带有一个附加的头`errorMessage`。下面的示例显示了这样的`errorMessage`:

```
errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB
```

|   |如果在路由定义中没有作为筛选参数提供，则默认的请求大小设置为 5MB。|
|---|--------------------------------------------------------------------------------------------------------|

### 6.27.`SetRequestHostHeader``GatewayFilter`工厂

在某些情况下，主机标题可能需要被重写。在这种情况下，`SetRequestHostHeader``GatewayFilter`工厂可以用指定的 vaue 替换现有的主机报头。过滤器接受`host`参数。下面的列表配置了`SetRequestHostHeader``GatewayFilter`:

示例 55.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: set_request_host_header_route
        uri: http://localhost:8080/headers
        predicates:
        - Path=/headers
        filters:
        - name: SetRequestHostHeader
          args:
            host: example.org
```

`SetRequestHostHeader``GatewayFilter`工厂将主机报头的值替换为`example.org`。

### 6.28.修改请求主体`GatewayFilter`工厂

你可以使用`ModifyRequestBody`过滤器过滤器来修改请求主体，然后再由网关向下游发送。

|   |这个过滤器只能通过使用 Java DSL 进行配置。|
|---|---------------------------------------------------------|

下面的清单显示了如何修改请求主体`GatewayFilter`:

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

static class Hello {
    String message;

    public Hello() { }

    public Hello(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
```

|   |如果请求没有正文，`RewriteFilter`将被传递`null`。应该返回`Mono.empty()`以分配请求中缺少的主体。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------|

### 6.29.修改响应主体`GatewayFilter`工厂

你可以使用`ModifyResponseBody`过滤器来修改响应主体，然后再将其发送回客户机。

|   |这个过滤器只能通过使用 Java DSL 进行配置。|
|---|---------------------------------------------------------|

下面的清单显示了如何修改响应主体`GatewayFilter`:

```
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
        .build();
}
```

|   |如果响应没有主体，则将传递`RewriteFilter`。应该返回`Mono.empty()`，以便在响应中分配一个缺少的主体。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------|

### 6.30.令牌中继`GatewayFilter`工厂

令牌中继是 OAuth2 使用者充当客户端并将传入的令牌转发给传出的资源请求的一种方式。使用者可以是纯客户机（如 SSO 应用程序）或资源服务器。

Spring Cloud网关可以将 OAuth2 访问令牌转发到其代理的服务的下游。要将此功能添加到 Gateway，你需要添加`TokenRelayGatewayFilterFactory`，如下所示：

app.java

```
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("resource", r -> r.path("/resource")
                    .filters(f -> f.tokenRelay())
                    .uri("http://localhost:9000"))
            .build();
}
```

或者这个

应用程序.YAML

```
spring:
  cloud:
    gateway:
      routes:
      - id: resource
        uri: http://localhost:9000
        predicates:
        - Path=/resource
        filters:
        - TokenRelay=
```

并且它将（除了登录用户并获取令牌之外）向下游传递身份验证令牌到服务（在这种情况下`/resource`）。

要为 Spring Cloud网关启用此功能，请添加以下依赖项

* `org.springframework.boot:spring-boot-starter-oauth2-client`

它是如何工作的？{githubmaster}/SRC/main/java/org/springframework/cloud/gateway/security/tokenrelaygatewayfilterfactory.java[filter]从当前经过身份验证的用户中提取一个访问令牌，并将其放在下游请求的请求头中。

有关完整的工作示例，请参见[这个项目](https://github.com/spring-cloud-samples/sample-gateway-oauth2login)。

|   |只有设置了适当的`spring.security.oauth2.client.*`属性才会创建`TokenRelayGatewayFilterFactory` Bean，这将触发创建`ReactiveClientRegistrationRepository` Bean。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |`TokenRelayGatewayFilterFactory`使用的`ReactiveOAuth2AuthorizedClientService`的默认实现使用内存中的数据存储。如果需要更健壮的解决方案，则需要提供自己的实现`ReactiveOAuth2AuthorizedClientService`。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 6.31.`CacheRequestBody``GatewayFilter`工厂

由于请求体流只能被读取一次，所以需要对请求体流进行缓存。你可以使用`CacheRequestBody`过滤器来缓存请求主体，然后再将其发送到下游，并从 exchagne 属性获取主体。

下面的清单显示了如何缓存请求主体`GatewayFilter`:

```
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("cache_request_body_route", r -> r.path("/downstream/**")
            .filters(f -> f.prefixPath("/httpbin")
                .cacheRequestBody(String.class).uri(uri))
        .build();
}
```

示例 56.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: cache_request_body_route
        uri: lb://downstream
        predicates:
        - Path=/downstream/**
        filters:
        - name: CacheRequestBody
          args:
            bodyClass: java.lang.String
```

`CacheRequestBody`将提取请求主体并将其转换为主体类（如`java.lang.String`，在前面的示例中定义）。然后将其放置在`ServerWebExchange.getAttributes()`中，并在`ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`中定义一个键。

|   |此过滤器仅适用于 HTTP 请求（包括 HTTPS）。|
|---|-----------------------------------------------------------|

### 6.32.默认过滤器

要添加筛选器并将其应用到所有路由，你可以使用`spring.cloud.gateway.default-filters`。此属性接受一个过滤器列表。以下清单定义了一组默认筛选器：

示例 57.application.yml

```
spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin
```

## 7.全局过滤器

`GlobalFilter`接口具有与`GatewayFilter`相同的签名。这些是有条件地应用于所有路由的特殊过滤器。

|   |该接口及其使用情况可能会在未来的里程碑版本中发生更改。|
|---|--------------------------------------------------------------------------------|

### 7.1.组合全局过滤器和`GatewayFilter`排序

当请求与路由匹配时，过滤 Web 处理程序将`GlobalFilter`的所有实例和`GatewayFilter`的所有特定于路由的实例添加到筛选链中。这个组合的过滤器链通过`org.springframework.core.Ordered`接口进行排序，你可以通过实现`getOrder()`方法来设置该接口。

Spring 由于云网关对于过滤器逻辑执行区分了“pre”和“post”阶段（参见[它是如何工作的](#gateway-how-it-works)），优先级最高的过滤器是“pre”阶段的第一个，“post”阶段的最后一个。

下面的清单配置了一个过滤器链：

例 58.示例 configuration.java

```
@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
```

### 7.2.前向路由滤波器

`ForwardRoutingFilter`在 exchange 属性`ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`中查找 URI。如果 URL 具有`forward`方案（例如`forward:///localendpoint`），则它使用 Spring `DispatcherHandler`来处理请求。请求 URL 的路径部分被转发 URL 中的路径覆盖。未修改的原始 URL 被追加到`ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR`属性中的列表中。

### 7.3.`ReactiveLoadBalancerClientFilter`

`ReactiveLoadBalancerClientFilter`在名为`ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`的 exchange 属性中查找 URI。如果 URL 具有`lb`方案（例如`lb://myservice`），则它使用 Spring Cloud`ReactorLoadBalancer`将名称（在本例中为`myservice`）解析为实际的主机和端口，并替换相同属性中的 URI。未修改的原始 URL 被追加到`ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR`属性中的列表中。过滤器还查看`ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR`属性，以查看它是否等于`lb`。如果是这样，也适用同样的规则。下面的清单配置了`ReactiveLoadBalancerClientFilter`:

示例 59.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**
```

|   |默认情况下，当`ReactorLoadBalancer`无法找到服务实例时，将返回一个`503`。<br/>通过设置`spring.cloud.gateway.loadbalancer.use404=true`，你可以将网关配置为返回一个`404`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |从`ReactiveLoadBalancerClientFilter`返回的`isSecure`值的`ServiceInstance`覆盖了<br/>向网关提出的请求中指定的方案。<br/>例如，如果请求通过`HTTPS`进入网关，但`ServiceInstance`表明它是不安全的，下游请求是在`HTTP`上提出的。<br/>相反的情况也可以适用。<br/>但是，如果`GATEWAY_SCHEME_PREFIX_ATTR`是在网关配置中为该路由指定的，则前缀被剥离，并且从路由 URL 中得到的方案覆盖了`ServiceInstance`配置。|
|---| |

|   |网关支持所有的负载平衡器功能。你可以在[Spring Cloud Commons documentation](https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer)中阅读有关它们的更多信息。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 7.4.Netty 路由过滤器

如果`ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`Exchange 属性中的 URL 具有`http`或`https`方案，则运行 Netty 路由过滤器。它使用 netty`HttpClient`发出下游代理请求。将响应放入`ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR`exchange 属性中，以便在以后的筛选器中使用。（还有一个实验性的`WebClientHttpRoutingFilter`，它执行相同的功能，但不需要 netty。

### 7.5.Netty 写响应过滤器

如果`ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR`exchange 属性中有一个 netty`HttpClientResponse`，则运行`NettyWriteResponseFilter`。它在所有其他过滤器完成并将代理响应写回网关客户机响应后运行。（还有一个实验性的`WebClientWriteResponseFilter`，它执行相同的功能，但不需要 netty。

### 7.6.`RouteToRequestUrl`过滤器

如果在`ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR`exchange 属性中有一个`Route`对象，则运行`RouteToRequestUrlFilter`。它根据请求 URI 创建一个新的 URI，但使用`Route`对象的 URI 属性进行更新。新的 URI 放在`ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`exchange 属性中。

如果 URI 有一个方案前缀，例如`lb:ws://serviceid`，则将从 URI 中剥离`lb`方案，并将其放置在`ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR`中，以便稍后在筛选链中使用。

### 7.7. The Websocket Routing Filter

如果位于`ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`Exchange 属性中的 URL 具有`ws`或`wss`方案，则运行 WebSocket 路由过滤器。它使用 Spring  WebSocket 基础设施向下游转发 WebSocket 请求。

你可以通过在 URI 前加上`lb`来实现 WebSockets 的负载平衡，比如`lb:ws://serviceid`。

|   |如果使用[SockJS](https://github.com/sockjs)作为普通 HTTP 的后备，则应该配置普通 HTTP 路由和 WebSocket 路由。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------|

下面的清单配置了一个 WebSocket 路由过滤器：

示例 60.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      # SockJS route
      - id: websocket_sockjs_route
        uri: http://localhost:3001
        predicates:
        - Path=/websocket/info/**
      # Normal Websocket route
      - id: websocket_route
        uri: ws://localhost:3001
        predicates:
        - Path=/websocket/**
```

### 7.8.网关指标过滤器

要启用网关度量，可以添加 Spring-boot-starter-actuator 作为项目依赖项。然后，默认情况下，只要属性`spring.cloud.gateway.metrics.enabled`未设置为`false`，网关指标过滤器就会运行。此过滤器添加了一个名为`spring.cloud.gateway.requests`的计时器度量，并带有以下标记：

* `routeId`:路径 ID。

* `routeUri`:将 API 路由到的 URI。

* `outcome`:结果，按[HttpStatus.series](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.Series.html)分类。

* `status`:返回给客户机的请求的 HTTP 状态。

* `httpStatusCode`:返回给客户机的请求的 HTTP 状态。

* `httpMethod`:用于请求的 HTTP 方法。

此外，通过属性`spring.cloud.gateway.metrics.tags.path.enabled`（默认情况下，设置为 false），你可以使用标记激活一个额外的度量指标：

* `path`:请求的路径。

然后可以从`/actuator/metrics/spring.cloud.gateway.requests`中获取这些指标，并且可以轻松地与 Prometheus 集成以创建[Grafana](https://docs.spring.io/spring-cloud-gateway/docs/3.1.1/reference/html/images/gateway-grafana-dashboard.jpeg)[dashboard](gateway-grafana-dashboard.json)。

|   |要启用 Prometheus 端点，请添加`micrometer-registry-prometheus`作为项目依赖项。|
|---|------------------------------------------------------------------------------------------------|

### 7.9.将交易所标记为路由

在网关路由了`ServerWebExchange`之后，它通过在 exchange 属性中添加`gatewayAlreadyRouted`，将该 exchange 标记为“路由”。一旦一个请求被标记为路由，其他路由过滤器将不会再次路由该请求，基本上跳过了该过滤器。有一些方便的方法，你可以使用它来标记一个交换为路由，或者检查一个交换是否已经被路由。

* `ServerWebExchangeUtils.isAlreadyRouted`接受一个`ServerWebExchange`对象，并检查它是否已被“路由”。

* `ServerWebExchangeUtils.setAlreadyRouted`接受一个`ServerWebExchange`对象，并将其标记为“路由”。

## 8.HttpHeadersFilters

HttpHeadersFilters 在向下游发送请求之前应用于请求，例如在`NettyRoutingFilter`中。

### 8.1.转发头过滤器

`Forwarded`headers filter 创建一个`Forwarded`header 以发送到下游服务。它将当前请求的`Host`头、方案和端口添加到任何现有的`Forwarded`头。

### 8.2.RemoveHopbyHop Headers 过滤器

`RemoveHopByHop`headers 过滤器从转发的请求中删除 header。被删除的头的默认列表来自[IETF](https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3)。

默认删除的标题是：

* 连接

* 保持活力

* 代理身份验证

* 代理授权

* TE

* 预告片

* 传输编码

* 升级

要更改这一点，请将`spring.cloud.gateway.filter.remove-hop-by-hop.headers`属性设置为要删除的头名称列表。

### 8.3.XForwarded Headers 过滤器

`XForwarded`headers filter 创建了各种`X-Forwarded-*`headers 以发送到下游服务。它使用`Host`报头、方案、端口和当前请求的路径来创建各种报头。

可以通过以下布尔属性（默认为 true）来控制单个标题的创建：

* `spring.cloud.gateway.x-forwarded.for-enabled`

* `spring.cloud.gateway.x-forwarded.host-enabled`

* `spring.cloud.gateway.x-forwarded.port-enabled`

* `spring.cloud.gateway.x-forwarded.proto-enabled`

* `spring.cloud.gateway.x-forwarded.prefix-enabled`

追加多个头可以由以下布尔属性控制（默认为 true）：

* `spring.cloud.gateway.x-forwarded.for-append`

* `spring.cloud.gateway.x-forwarded.host-append`

* `spring.cloud.gateway.x-forwarded.port-append`

* `spring.cloud.gateway.x-forwarded.proto-append`

* `spring.cloud.gateway.x-forwarded.prefix-append`

## 9.TLS 和 SSL

网关可以通过遵循通常的 Spring 服务器配置来监听 HTTPS 上的请求。下面的示例展示了如何做到这一点：

示例 61.application.yml

```
server:
  ssl:
    enabled: true
    key-alias: scg
    key-store-password: scg1234
    key-store: classpath:scg-keystore.p12
    key-store-type: PKCS12
```

你可以将网关路由到 HTTP 和 HTTPS 后端。如果要路由到 HTTPS 后端，则可以通过以下配置将网关配置为信任所有下游证书：

示例 62.application.yml

```
spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          useInsecureTrustManager: true
```

使用不安全的信任管理器不适合于生产。对于产品部署，你可以使用一组已知证书来配置网关，它可以通过以下配置来信任这些证书：

示例 63.application.yml

```
spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          trustedX509Certificates:
          - cert1.pem
          - cert2.pem
```

如果 Spring Cloud网关没有提供受信任的证书，则使用默认的信任存储区（你可以通过设置`javax.net.ssl.trustStore`系统属性来覆盖该存储区）。

### 9.1.TLS 握手

网关维护一个用于路由到后端的客户机池。当通过 HTTPS 进行通信时，客户机发起 TLS 握手。与此握手相关的超时次数很多。你可以配置这些超时可以配置（默认显示）如下：

示例 64.application.yml

```
spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          handshake-timeout-millis: 10000
          close-notify-flush-timeout-millis: 3000
          close-notify-read-timeout-millis: 0
```

## 10.配置(#configuration)

Spring Cloud网关的配置由`RouteDefinitionLocator`实例的集合驱动。下面的清单显示了`RouteDefinitionLocator`接口的定义：

例 65.RouteDefinitionLocator.java

```
public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
}
```

默认情况下，`PropertiesRouteDefinitionLocator`通过使用 Spring boot 的`@ConfigurationProperties`机制加载属性。

较早的配置示例都使用快捷方式表示，它使用位置参数而不是命名参数。以下两个例子是等价的：

示例 66.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: setstatus_route
        uri: https://example.org
        filters:
        - name: SetStatus
          args:
            status: 401
      - id: setstatusshortcut_route
        uri: https://example.org
        filters:
        - SetStatus=401
```

对于网关的某些用法，属性是足够的，但是一些生产用例受益于从外部源（例如数据库）加载配置。未来的里程碑版本将具有基于 Spring 数据存储库的`RouteDefinitionLocator`实现，例如 Redis、MongoDB 和 Cassandra。

### 10.1.路由定义度量(#routedefinition-metrics)

要启用`RouteDefinition`指标，请添加 Spring-boot-starter-actuator 作为项目依赖项。然后，默认情况下，只要将属性`spring.cloud.gateway.metrics.enabled`设置为`true`，这些指标就可用。将添加一个名为`spring.cloud.gateway.routes.count`的规范度量，其值是`RouteDefinitions`的数量。该指标将从`/actuator/metrics/spring.cloud.gateway.routes.count`开始提供。

## 11.路由元数据配置(#route-metadata-configuration)

你可以通过使用元数据为每个路由配置附加参数，如下所示：

示例 67.application.yml

```
spring:
  cloud:
    gateway:
      routes:
      - id: route_with_metadata
        uri: https://example.org
        metadata:
          optionName: "OptionValue"
          compositeObject:
            name: "value"
          iAmNumber: 1
```

你可以从 Exchange 获取所有元数据属性，如下所示：

```
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// get all metadata properties
route.getMetadata();
// get a single metadata property
route.getMetadata(someKey);
```

## 12.HTTP 超时配置(#http-timeouts-configuration)

可以为所有路由配置 HTTP 超时（响应和连接），并为每个特定的路由重写。

### 12.1.全球超时(#global-timeouts)

要配置全局 HTTP 超时：`connect-timeout`必须以毫秒为单位指定。`response-timeout`必须指定为 java.time.duration

全局 HTTP 超时示例

```
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s
```

### 12.2.每条路线超时(#per-route-timeouts)

要配置每路由超时：`connect-timeout`必须以毫秒为单位指定。`response-timeout`必须以毫秒为单位指定。

通过配置进行每路由 HTTP 超时配置

```
      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          - name: Path
            args:
              pattern: /delay/{timeout}
        metadata:
          response-timeout: 200
          connect-timeout: 200
```

使用 Java DSL 的每路由超时配置

```
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.CONNECT_TIMEOUT_ATTR;
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;

      @Bean
      public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder){
         return routeBuilder.routes()
               .route("test1", r -> {
                  return r.host("*.somehost.org").and().path("/somepath")
                        .filters(f -> f.addRequestHeader("header1", "header-value-1"))
                        .uri("http://someuri")
                        .metadata(RESPONSE_TIMEOUT_ATTR, 200)
                        .metadata(CONNECT_TIMEOUT_ATTR, 200);
               })
               .build();
      }
```

带负值的每路由`response-timeout`将禁用全局`response-timeout`值。

```
      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          - name: Path
            args:
              pattern: /delay/{timeout}
        metadata:
          response-timeout: -1
```

### 12.3.Fluent Java Routes API(#fluent-java-routes-api)

为了允许在 Java 中进行简单的配置，`RouteLocatorBuilder` Bean 包含了一个 Fluent API。下面的清单展示了它的工作原理：

例 68.GatewaySampleApplication.java

```
// static imports from GatewayFilters and RoutePredicates
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
    return builder.routes()
            .route(r -> r.host("**.abc.org").and().path("/image/png")
                .filters(f ->
                        f.addResponseHeader("X-TestHeader", "foobar"))
                .uri("http://httpbin.org:80")
            )
            .route(r -> r.path("/image/webp")
                .filters(f ->
                        f.addResponseHeader("X-AnotherHeader", "baz"))
                .uri("http://httpbin.org:80")
                .metadata("key", "value")
            )
            .route(r -> r.order(-1)
                .host("**.throttle.org").and().path("/get")
                .filters(f -> f.filter(throttle.apply(1,
                        1,
                        10,
                        TimeUnit.SECONDS)))
                .uri("http://httpbin.org:80")
                .metadata("key", "value")
            )
            .build();
}
```

这种样式还允许更多的自定义谓词断言。由`RouteDefinitionLocator`bean 定义的谓词使用逻辑`and`进行组合。通过使用 Fluent Java API，你可以在`Predicate`类上使用`and()`、`or()`和`negate()`运算符。

### [12.4.路由定义定位器]（#the-discoveryclient-route-definition-locator）

你可以将网关配置为基于在`DiscoveryClient`兼容服务注册中心注册的服务创建路由。

要启用此功能，请设置`spring.cloud.gateway.discovery.locator.enabled=true`，并确保在 Classpath 上启用了实现（例如 Netflix Eureka、Consul 或 ZooKeeper）。

#### 12.4.1.为`DiscoveryClient`路由配置谓词和筛选器（#configuring-predicates-and-filters-for-discoveryclient-route）

默认情况下，网关为使用`DiscoveryClient`创建的路由定义一个谓词和过滤器。

缺省谓词是用模式`/serviceId/**`定义的路径谓词，其中`serviceId`是来自`DiscoveryClient`的服务的 ID。

默认的过滤器是一个重写路径过滤器，regex`/serviceId/?(?<remaining>.*)`和替换`/${remaining}`。这将在向下游发送请求之前从路径中剥离服务 ID。

如果要自定义`DiscoveryClient`路由所使用的谓词或筛选器，请设置`spring.cloud.gateway.discovery.locator.predicates[x]`和`spring.cloud.gateway.discovery.locator.filters[y]`。这样做时，如果你想保留该功能，则需要确保包含前面显示的缺省谓词和筛选器。下面的示例显示了这是什么样子的：

示例 69.application.properties

```
spring.cloud.gateway.discovery.locator.predicates[0].name: Path
spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'"
spring.cloud.gateway.discovery.locator.predicates[1].name: Host
spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'"
spring.cloud.gateway.discovery.locator.filters[0].name: CircuitBreaker
spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId
spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath
spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/?(?<remaining>.*)'"
spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"
```

## 13.反应堆网络访问日志(#reactor-netty-access-logs)

要启用反应堆网络访问日志，请设置`-Dreactor.netty.http.server.accessLogEnabled=true`。

|   |它必须是一个 Java 系统属性，而不是一个 Spring 引导属性。|
|---|--------------------------------------------------------------|

你可以将日志系统配置为具有一个单独的访问日志文件。下面的示例创建了一个注销配置：

示例 70.logback.xml

```
    <appender name="accessLog" class="ch.qos.logback.core.FileAppender">
        <file>access_log.log</file>
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="accessLog" />
    </appender>

    <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
        <appender-ref ref="async"/>
    </logger>
```

## 14.CORS 配置(#cors-configuration)

你可以配置网关来控制 CORS 行为。“全局”CORS 配置是 URL 模式到[[ Spring Framework`CorsConfiguration`]（https://DOCS. Spring.io/ Spring/DOCS/5.0.x/javadoc-api/org/springframework/web/cors/corsconfiguration.html）的映射。以下示例配置 CORS:

示例 71.application.yml

```
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET
```

在前面的示例中，对于所有 GET 请求的路径，允许从源自`docs.spring.io`的请求发出 CORS 请求。

要为某些网关路由谓词不处理的请求提供相同的 CORS 配置，请将`spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping`属性设置为`spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping`。当你试图支持 CORS 的 Preflight 请求，而你的路由谓词不求值到`true`时，这是有用的，因为 HTTP 方法是`options`。

## 15.执行机构 API(#actuator-api)

执行器端点允许你监视 Spring Cloud网关应用程序并与之交互。要实现远程访问，端点必须是应用程序属性中的[enabled](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#production-ready-endpoints-enabling-endpoints)和[通过 HTTP 或 JMX 公开](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#production-ready-endpoints-exposing-endpoints)。下面的清单展示了如何做到这一点：

示例 72.application.properties

```
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
```

### 15.1.详细执行器格式(#verbose-actuator-format)

Spring Cloud网关中添加了一种新的、更详细的格式。它为每条路由添加了更多细节，允许你查看与每条路由相关的谓词和筛选器，以及可用的任何配置。以下示例配置`/actuator/gateway/routes`:

```
[
  {
    "predicate": "(Hosts: [**.addrequestheader.org] && Paths: [/headers], match trailing slash: true)",
    "route_id": "add_request_header_test",
    "filters": [
      "[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
      "[[AddRequestHeader X-Request-Foo = 'Bar'], order = 1]",
      "[[PrefixPath prefix = '/httpbin'], order = 2]"
    ],
    "uri": "lb://testservice",
    "order": 0
  }
]
```

默认情况下启用此功能。要禁用它，请设置以下属性：

示例 73.application.properties

```
spring.cloud.gateway.actuator.verbose.enabled=false
```

在将来的版本中，这将默认为`true`。

### 15.2.检索路由过滤器(#retrieving-route-filters)

本节详细介绍了如何检索路由过滤器，包括：

* [全局过滤器](#gateway-global-filters)

* [[gateway-route-filters]]（#gateway-route-filters）

#### 15.2.1.全局过滤器(#gateway-global-filters)

要检索应用于所有路由的`GET`，请对`/actuator/gateway/globalfilters`发出`GET`请求。由此产生的反应类似于以下情况：

```
{
  "org.spring[email protected]77856cc5": 10100,
  "o[email protected]4f6fd101": 10000,
  "or[email protected]32d22650": -1,
  "[email protected]6459d9": 2147483647,
  "[email protected]5e0": 2147483647,
  "[email protected]d23": 0,
  "org.s[email protected]135064ea": 2147483637,
  "[email protected]23c05889": 2147483646
}
```

响应包含已到位的全局过滤器的详细信息。对于每个全局过滤器，有一个字符串表示过滤器对象（例如，`org.spring[[email protected]](/cdn-cgi/l/email-protection)77856cc5`）和相应的[order](#gateway-combined-global-filter-and-gatewayfilter-ordering)在过滤器链中。}

#### 15.2.2.路由过滤器(#gateway-route-filters)

要检索应用于路由的[`GatewayFilter`工厂]（#gatewayfilter-factories），对`GET`请求`/actuator/gateway/routefilters`。由此产生的反应类似于以下情况：

```
{
  "[[email protected] configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
  "[[email protected] configClass = Object]": null,
  "[[email protected] configClass = Object]": null
}
```

响应包含应用于任何特定路径的`GatewayFilter`工厂的详细信息。对于每个工厂，都有一个对应对象的字符串表示（例如，`GatewayFilter`）。请注意，`null`值是由于端点控制器的实现不完整造成的，因为它试图设置过滤器链中对象的顺序，这不适用于`GatewayFilter`工厂对象。

### 15.3.刷新路径缓存(#refreshing-the-route-cache)

要清除路由缓存，请对`POST`请求`/actuator/gateway/refresh`。该请求返回一个没有响应体的 200.

### 15.4.检索在网关中定义的路由(#retrieving-the-routes-defined-in-the-gateway)

要检索在网关中定义的路由，请对`GET`请求`/actuator/gateway/routes`。由此产生的反应类似于以下情况：

```
[{
  "route_id": "first_route",
  "route_object": {
    "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/[email protected]",
    "filters": [
      "OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/[email protected], order=0}"
    ]
  },
  "order": 0
},
{
  "route_id": "second_route",
  "route_object": {
    "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/[email protected]",
    "filters": []
  },
  "order": 0
}]
```

响应包含网关中定义的所有路由的详细信息。下表描述了响应的每个元素（每个都是路由）的结构：

|          Path          | Type |说明|
|------------------------|------|-------------------------------------------------------------------------------|
|       `route_id`       |String|路线 ID。|
|`route_object.predicate`|Object|路线谓词。|
| `route_object.filters` |Array |[`GatewayFilter`工厂]（#gatewayfilter-factories）应用于该路由。|
|        `order`         |Number|路线顺序。|

### 15.5.检索有关特定路线的信息(#gateway-retrieving-information-about-a-particular-route)

要检索有关单个路由的信息，请对`GET`请求`/actuator/gateway/routes/{id}`（例如，`/actuator/gateway/routes/first_route`）。由此产生的反应类似于以下情况：

```
{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"/first"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}
```

下表描述了响应的结构：

|    Path    | Type |说明|
|------------|------|------------------------------------------------------------------------------------------------------|
|    `id`    |String|路线 ID。|
|`predicates`|Array |路由谓词的集合。每个项定义给定谓词的名称和参数。|
| `filters`  |Array |应用于路由的过滤器的集合。|
|   `uri`    |String|路线的目的地 URI。|
|  `order`   |Number|路线顺序。|

### 15.6.创建和删除特定的路由(#creating-and-deleting-a-particular-route)

要创建一个路由，使用指定路由字段的 JSON 主体对`POST`请求`/gateway/routes/{id_route_to_create}`（参见[检索有关特定路线的信息](#gateway-retrieving-information-about-a-particular-route)）。

要删除一个路由，请对`DELETE`请求`/gateway/routes/{id_route_to_delete}`。

### 15.7.回顾：所有端点的列表(#recap-the-list-of-all-endpoints)

下面的 FolloiWNG 表总结了 Spring Cloud网关执行器端点（请注意，每个端点都以`/actuator/gateway`作为基本路径）：

|      ID       |HTTP Method|说明|
|---------------|-----------|-----------------------------------------------------------------------------|
|`globalfilters`|    GET    |显示应用于路由的全局筛选器列表。|
|`routefilters` |    GET    |显示应用于特定路径的`GatewayFilter`工厂的列表。|
|   `refresh`   |   POST    |清除路由缓存。|
|   `routes`    |    GET    |显示在网关中定义的路由列表。|
| `routes/{id}` |    GET    |显示有关特定路线的信息。|
| `routes/{id}` |   POST    |为网关添加了一条新的路径。|
| `routes/{id}` |  DELETE   |从网关删除现有的路由。|

### 15.8.在多个网关实例之间共享路由(#sharing-routes-between-multiple-gateway-instances)

Spring Cloud网关提供了两种`RouteDefinitionRepository`实现方式。第一个是`InMemoryRouteDefinitionRepository`，它只存在于一个网关实例的内存中。这种类型的存储库不适合跨多个网关实例填充路由。

为了跨 Spring Cloud网关实例的集群共享路由，可以使用`RedisRouteDefinitionRepository`。要启用这种存储库，以下属性必须设置为 true:`spring.cloud.gateway.redis-route-definition-repository.enabled`同样，对于 RedisrateLimiter 过滤器工厂，它需要使用 Spring-boot-starter-data-redis-active Spring boot starter。

## 16.故障排除(#troubleshooting)

本节介绍了在使用 Spring Cloud网关时可能出现的常见问题。

### 16.1.日志级别(#log-levels)

以下记录器可能在`DEBUG`和`TRACE`级别包含有价值的故障排除信息：

* `org.springframework.cloud.gateway`

* `org.springframework.http.server.reactive`

* `org.springframework.web.reactive`

* `org.springframework.boot.autoconfigure.web`

* `reactor.netty`

* `redisratelimiter`

### 16.2.窃听(#wiretap)

反应堆网络`HttpClient`和`HttpServer`可以启用窃听功能。当与将`reactor.netty`日志级别设置为`DEBUG`或`TRACE`相结合时，它可以记录信息，例如通过连接发送和接收的标题和主体。要启用窃听，分别为`HttpServer`和`HttpClient`设置`spring.cloud.gateway.httpclient.wiretap=true`。

## 17.开发者指南(#developer-guide)

这些是编写网关的一些自定义组件的基本指南。

### 17.1.编写自定义路由谓词工厂(#writing-custom-route-predicate-factories)

为了编写路由谓词，你需要将`RoutePredicateFactory`实现为 Bean。有一个名为`AbstractRoutePredicateFactory`的抽象类，你可以对其进行扩展。

MyRoutepredicateFactory.java

```
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

    public MyRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // grab configuration from Config object
        return exchange -> {
            //grab the request
            ServerHttpRequest request = exchange.getRequest();
            //take information from the request to see if it
            //matches configuration.
            return matches(config, request);
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}
```

### 17.2.编写自定义网关过滤器工厂(#writing-custom-gatewayfilter-factories)

要编写`GatewayFilter`，你必须将`GatewayFilterFactory`实现为 Bean。你可以扩展一个名为`AbstractGatewayFilterFactory`的抽象类。下面的例子说明了如何做到这一点：

例 74.Pregatewayfilterfactory.java

```
@Component
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {

    public PreGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            //If you want to build a "pre" filter you need to manipulate the
            //request before calling chain.filter
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //use builder to manipulate the request
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}
```

Postgatewayfilterfactory.java

```
@Component
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {

    public PostGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                //Manipulate the response in some way
            }));
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}
```

#### 17.2.1.在配置中命名自定义过滤器和引用(#naming-custom-filters-and-references-in-configuration)

自定义过滤器类名称应该以`GatewayFilterFactory`结尾。

例如，要在配置文件中引用名为`Something`的过滤器，该过滤器必须位于名为`SomethingGatewayFilterFactory`的类中。

|   |可以创建一个没有`GatewayFilterFactory`后缀的网关过滤器，例如`class AnotherThing`。在配置文件中，这个过滤器可以被<br/>引用为`AnotherThing`。这是**不是**所支持的命名<br/>约定，该语法可能会在将来的版本中删除。请更新过滤器[](#building-a-simple-gateway-by-using-spring-mvc-or-webflux)名称以使其兼容。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 17.3.编写自定义全局过滤器(#writing-custom-global-filters)

要编写自定义的全局过滤器，你必须将`class AnotherThing`接口实现为 Bean。这将过滤器应用于所有请求。

以下示例分别展示了如何设置全局 pre 和 post 过滤器：

```
@Bean
public GlobalFilter customGlobalFilter() {
    return (exchange, chain) -> exchange.getPrincipal()
        .map(Principal::getName)
        .defaultIfEmpty("Default User")
        .map(userName -> {
          //adds header to proxied request
          exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
          return exchange;
        })
        .flatMap(chain::filter);
}

@Bean
public GlobalFilter customGlobalPostFilter() {
    return (exchange, chain) -> chain.filter(exchange)
        .then(Mono.just(exchange))
        .map(serverWebExchange -> {
          //adds header to response
          serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
              HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
          return serverWebExchange;
        })
        .then();
}
```

## 18. Building a Simple Gateway by Using Spring MVC or Webflux(#building-a-simple-gateway-by-using-spring-mvc-or-webflux)

|   |下面描述了一种替代样式的网关。以前的文件都不适用于下面的内容。|
|---|--------------------------------------------------------------------------------------------------------------|

Spring Cloud网关提供了一种名为`ProxyExchange`的实用对象。你可以在常规的 Spring Web 处理程序中使用它作为方法参数。它通过镜像 HTTP 动词的方法支持基本的下游 HTTP 交换。对于 MVC，它还支持通过`forward()`方法转发到本地处理程序。要使用`ProxyExchange`，在 Classpath 中包含正确的模块（`spring-cloud-gateway-mvc`或`spring-cloud-gateway-webflux`）。

下面的 MVC 示例将请求代理到`/test`下游的远程服务器：

```
@RestController
@SpringBootApplication
public class GatewaySampleApplication {

    @Value("${remote.home}")
    private URI home;

    @GetMapping("/test")
    public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {
        return proxy.uri(home.toString() + "/image/png").get();
    }

}
```

下面的示例对 WebFlux 做了相同的处理：

```
@RestController
@SpringBootApplication
public class GatewaySampleApplication {

    @Value("${remote.home}")
    private URI home;

    @GetMapping("/test")
    public Mono<ResponseEntity<?>> proxy(ProxyExchange<byte[]> proxy) throws Exception {
        return proxy.uri(home.toString() + "/image/png").get();
    }

}
```

`ProxyExchange`上的便利方法使处理程序方法能够发现并增强传入请求的 URI 路径。例如，你可能希望提取路径的尾随元素，以便向下游传递它们：

```
@GetMapping("/proxy/path/**")
public ResponseEntity<?> proxyPath(ProxyExchange<byte[]> proxy) throws Exception {
  String path = proxy.path("/proxy/path/");
  return proxy.uri(home.toString() + "/foos/" + path).get();
}
```

Spring MVC 和 WebFlux 的所有特性都可用于网关处理程序方法。因此，例如，你可以插入请求头和查询参数，并且可以通过映射注释中的声明来约束传入的请求。有关这些特性的更多详细信息，请参见 Spring MVC 中`ProxyExchange`的文档。

可以使用`ProxyExchange`上的`header()`方法向下游响应添加标题。

你还可以通过向`get()`方法（和其他方法）添加一个映射器来操作响应头（以及响应中喜欢的任何其他方法）。映射器是一个`Function`，它接收传入的`ResponseEntity`并将其转换为传出的。

为“敏感”头（默认情况下，`cookie`和`authorization`）和“代理”头（`x-forwarded-*`）提供了一流的支持。

## 19.配置属性(#configuration-properties)

要查看所有 Spring Cloud网关相关配置属性的列表，请参见[附录](appendix.html)。