Spring Cloud Gateway官方文档试译

Spring Cloud Gateway 官方文档试译

本文尝试着翻译一下Spring Cloud Gateway的官方文档,对应版本2.2.4.BUILD-SNAPSHOT。旨在加深本人对Spring Cloud Gateway的理解和使用。

如果不幸被其他人看到了,那烦请高抬贵嘴,第一次翻译,难免有不足之处。

2.2.4.BUILD-SNAPSHOT

此项目孵化了一个基于Spring生态系统:包括,Spring 5,Spring Boot 2和Project Reactor的API网关。Spring Cloud Gateway旨在提供一个简单,有效的路由API的方式,并提供了安全,监控/指标,以及弹性相关的横切关注。

如何引入Spring Cloud Gateway

如果要在自己的项目中引入Spring Cloud Gateway,你只需要在pom(以maven为例)中引入groupId为org/springframeword.cloud,artifact id为spring-cloud-starter-gateway的spring cloud starter即可。可以访问这里,获取更新详细的信息。

注意:Spring Cloud Gateway 是基于Spring Boot 2.X,Spring WebFlux和Project Reactor的。因此,大部分常见的同步库(比如,Spring Data和Spring Security)和模式可能不适用于Spring Cloud Gateway。如果你不熟悉这些项目,我们建议你在使用Spring Cloud Gateway之前,先阅读下他们的官方文件,预先对一些概念有些了解。

Spring Cloud Gateway 依赖Srping Boot 和 Spring WebFlux提供的Netty 运行环境。它不适用于传统的Servlet容器(比如低版本的tomcat等),也不适合打成WAR包。

概念释义

  • 路由:是网关的基本构件。它由一个ID,一个目标URI,一些列的断言和一系列的过滤器组成。如果断言威震,则路由匹配,目标URI会被访问。
  • 断言:这是一个Java 8 中的断言,输入类型是Spring框架的ServerWebExchange。它可以匹配来自HTTP请求的任意内容,比如,请求头或者参数。
  • 过滤器:它们是Spring框架的GatewayFilter,可以通过一些特定的工厂创建。我们可以通过它们来修改请求或者响应。

Spring Cloud Gateway 如何工作

下图从总体上概述了Spring Cloud Gateway的工作方式:

Spring Cloud Gateway工作流

客户端向Spring Cloud Gateway发出请求。如果Gateway Hanlder mapping认为请求与路由匹配,那么会将请求转发给Gateway Web Handler。Gateway Web Handler会运行一系列针对这个请求的过滤器。过滤器由虚线分隔的原因是,过滤器可以在发情代理请求之前和之后运行。在发出代理请求之前,执行所有前置(“pre”)过滤器。在发出代理请求之火,执行所有后置(“post”)过滤器。

配置路由断言工厂和网关路由器断言工厂

有两种方法可以配置断言和过滤器:快捷配置和完全展开配置。下面的大多数示例都是用快捷配置。

名称和参数名称会在每个部分的第一句或者第二句作为代码列出。参数通常按照快捷配置所需的顺序列出。

快捷配置

快捷配置方式由路由器名称作为起始,后跟等号(=),再跟着由逗号(,)分隔的参数。

application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue

上面的例子定义了一个Cookie路由断言工厂。它接收两个参数,cookie名称,mycookie和需要匹配的cookie值mycookievalue

全扩展配置

全扩展配置看起来更像标准的yaml 键值对配置。通常来说,会有一个name的键,对应一个args的值。args值一般是一系列的键值对,用来配置断言或者过滤器。

application.yml

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue

上面是上一个Cookie断言的全扩展配置版本。

路由断言工厂

Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包含了许多内置的路由断言工厂。所有这些断言都与HTTP请求的不同属性匹配。你可以任意组合多个路由断言工厂使用。

After 路由断言工厂

After路由断言工厂接收一个时间参数,datetime(java ZonedDateTime对象)。这个路由匹配指定时间之后的请求。

示例1:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]

这个路由匹配任何在时间2017-01-20T17:42:47.789-07:00[America/Denver的请求。

Before 路由断言工厂

Before路由断言工厂,接收一个时间参数,datetime(java ZonedDateTime对象)。这个路由匹配任意在这个时间点之前的请求。

示例2:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]

这个路由匹配所有在这个时间点(2017-01-20T17:42:47.789-07:00[America/Denver])之前的请求。

Between 路由断言工厂

Between路由断言工厂接收两个参数,datetime1(java ZonedDateTime对象)和datetime2(java ZonedDateTime对象)。这个断言匹配所有在datetime1之后,datetime2之前的请求。时间datetime2一定要在datetime1之前。

示例3:application.yml

1
2
3
4
5
6
7
8
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]

上面的路由匹配任何间于时间2017-01-20T17:42:47.789-07:00[America/Denver]和时间2017-01-21T17:42:47.789-07:00[America/Denver]之间的请求。

Cookie路演断言工厂接收两个参数,cookie的nameregexp(Java的正则表达是)。这个路由断言工厂匹配那些Cookie名称匹配,并且Cookie值匹配正则表达式(regexp)的请求。

示例4:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p

这个路由匹配那些具有名为chocolate,值匹配ch.p的cookie的请求。

Header 路由断言工厂

Header路由断言工厂接收两个参数,请求头名称name和请求头的值regexp(Java正则表达式)。这个路由匹配所有请求头名称为name,请求头值匹配regexp的请求。

示例5:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+

这个路由匹配所有请求中包含请求头X-Request-Id,请求头值为1个或多个数字(\d+)的请求。

Host 路由断言工厂

Host路由断言工厂接收一个参数:主机名字列表。主机名字以逗号分割,名字内可以用.作为分割符。

示例6:application.yml

1
2
3
4
5
6
7
8
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模板中抽取参数作为键值对,存入ServerWebExchange.getAttributes(),存入键名为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE

Method 路由断言工厂

Method路由断言工厂接收一个methods参数。该参数可以是一个或者多个HTTP方法。

示例6:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST

上述的路由匹配所有请求方法为GET或者POST的请求。

Path 路由断言工厂

Path路由断言工厂接收两个参数:一个路径列表和一个可选的标记参数matchOptionalTrailingSeparator.

示例8:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}

上述路由匹配下面的这些路径/red/1或者/red/blue抑或是/blue/green

这个路由也会从URI模板中提取参数,作为键值对,存入ServerWebExchange.getAttributes()的名为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE的属性值中。

下面的代码演示了如何获取segement的值:

1
2
3
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

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

Query 路由断言工厂

Query路由断言工厂接收两个参数:必选的param和可选的regexp(Java正则表达式)。

示例9:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green

上述的路由匹配,请求查询参数中具有green这个参数的请求。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.

上述的路由匹配请求中具有参数名为red和参数值符合gree的请求。例如,请求https://example.org/hello?red=greet。就会被匹配上。

RemoteAddr Route Predicate Factory

RemoteAddr路由断言工厂接收一个sources列表,作为参数。参数以CIDR形式表示。

示例10:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24

上述路由匹配远程地址为192.168.1.1/24范围内的请求,比如,192.168.1.10

Weight Route Predicate Factory

Weight路由断言工厂接收两个参数:groupweight(int型)。权重是根据每个组(group)计算的。

示例11:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
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,剩下的20%左右导向weightlow.org。

Modifying the Way Remote Addresses Are Resplved

默认情况下,RemoteAddr路由断言工厂使用请求中的远程地址。如果Spring Cloud Gateway身处在一个代理层之后,路由就无法拿到真实的客户端地址。

可以通过设置自定义的RemoteAddressResolver来自定义解析远程地址的方式。Spring Cloud Gateway随附了一个基于X-Forwarded-For请求头的XForwardedRemoteAddressResolver的非默认远程地址解析器。

XForwardedRemoteAddressResolver有两个静态的构造方法。它们使用了不同的实现方式:

  • XForwardedRemoteAddressResolver::trustAll:返回一个RemoteAddressResolver。它获取从X-Forwarded-For请求头中第一个发现的IP地址。这种方法是存在弱点的。一个恶意的客户端可以给X-Forwarded-For设置一个任意初始值,该Resolver就会接收该初始值。
  • XForwardedRemoteAddressResolver::maxTrustedIndex:采用与Spring Cloud Gateway前面运行的受信任基础架构数量相关的索引。例如,如果只能通过HAProxy访问Spring Cloud Gateway,则应使用值1。如果在访问Spring Cloud Gateway之前需要两跳可信基础架构,则应使用值2。

考虑如下请求头值:

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

以下maxTrustedIndex值产生以下远程地址:

maxTrustedIndex result
[Integer.MIN_VALUE,0] (invalid, IllegalArgumentException during initialization)
1 0.0.0.3
2 0.0.0.2
3 0.0.0.1
[4, Integer.MAX_VALUE] 0.0.0.1

下面的例子展示用Java代码如何实现相同的配置:

1
2
3
4
5
6
7
8
9
10
11
12
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")
)

网关过滤器工厂

路由过滤器允许我们对传入的请求或者传出的相应进行修改。路由过滤器适用于特定的路由。Spring Cloud Gateway内置了许多GatewayFilter工厂。

AddRequestHeader

此路由器工厂接收一个namevalue作为参数。

示例13:application.yml

1
2
3
4
5
6
7
8
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中获取变量。变量可以在过滤器中使用,并在运行期间被扩展。如下:

示例14:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeader=X-Request-Red, Blue-{segment}

AddRequestParameter

这个过滤器接收一个name和一个value作为参数。

示例15:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue

上述配置将会在所有符合条件的请求中,加上red=blue的查询参数。

AddrequestParameter过滤器也可以从匹配的路径或者主机中获取参数。参数可以用来配置过滤器,并在运行期间扩展。

示例16:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}

AddResponseHeader

此过滤器接收一个namevalue作为参数,它会在匹配的相应上加上配置的响应头:

示例17:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue

AddResponseHeader过滤器也可以从匹配的路径或者主机中获取参数。参数可以用来配置过滤器,并在运行期间扩展。

示例18:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddResponseHeader=foo, bar-{segment}

DedupeResponseHeader

这个过滤器接收一个name参数和一个可选的strategy参数。name参数由一系列的响应头名称组成,以空格分隔。

示例19:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

符合这个条件的响应头将会删除重复的Access-Control-Allow-CredentialsAccess-Control-Allow-Origin

strategy的取值RETAIN_FIRST(default)RETAIN_LAST,RETAIN_UNIQUE

Hystrix

注意:Netflix已经将Hystrix置于维护模式了,未来的版本中会被移除。建议使用Spring Cloud CircuitBreaker Gateway Filter。

Hystrix 是 Netflix 的库,实现了断路器模式。Hystrix GatewayFilter允许您将断路器引入网关路由,保护您的服务免受级联故障的影响,并允许在发生下游故障时提供回退响应。

在项目中引用Hsytrix过滤器,在依赖文件中添加spring-cloud-starter-netflix-hystrix依赖即可。

Hystrix过滤器工厂接收一个name参数。这个参数代表了HystrixCommand的名字。

示例20:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: https://example.org
filters:
- Hystrix=myCommandName

这些逻辑全部被封装在一个名为myCommandName的HystrixCommand中。

Hystrix过滤器还可以接收一个可选的回退uri参数-fallbackUri。但目前仅支持forward:。如果回退被处罚了,那么请求将被转发到匹配的URI控制器。

示例21:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingserviceendpoint
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/incaseoffailureusethis
- RewritePath=/consumingserviceendpoint, /backingserviceendpoint

当回退被调用时,请求将会被转发到/incaseofffailureuse。注意:这个示例还演示了Sping Cloud Netflix Ribbon 负载均衡的功能(uri中的lb前缀)。

此过滤器主要应用场景是使用回退uri(fallbackUri)路由到内部的控制器或者处理程序。但是,还可以将请求重新路由到外部程序的控制器或者处理程序。如下所示:

示例22:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback

上面这个例子中,没有配置回退(fallback)的处理器。但是,它配置了一个外部应用的处理器,注册在localhost:9994下。

伴着请求被转发到fallback,Hystrix也提供了造成这次回退的原因(Throwable)。它被存储在ServerWebExchangeServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR属性中。

对于外部控制器/处理程序场景,可以添加包含异常详细信息的请求头。可以在这里找到有关这样做的更多信息。

可以使用应用程序属性将 Hystrix 设置(如超时)配置为全局默认值,也可以在路由上配置,如 Hystrix wiki 上所述。

若要为前面显示的示例路由设置五秒超时,可以使用以下配置:

示例23:application.yml

1
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

Spring Cloud CircuitBreaker

此过滤器使用Spring Cloud CircuitBreaker API将网管路由封装在断路器中。Srping Cloud CircuirBreaker可与Hystrix和Resilience4j一起使用。但由于Netflix已经将Hystrix置于维护模式,因此,建议使用Resilience4j。

将下面两个依赖中的任意一个添加进来,就可以开启Spring Cloud CircuitBreaker 过滤器了:spring-cloud-starter-circuitbreaker-reactor-resilience4jspring-cloud-starter-netflix-hystrix

示例24:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: https://example.org
filters:
- CircuitBreaker=myCircuitBreaker

参加使用的对应组件的文档,如果要配置断路器的话:

Spring Cloud CircuitBreaker过滤器也可以接收一个可选的参数fallbackUri。当前,只支持forward:。如果回退了,那么请求就会被发到对应API的控制器去。

示例25:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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代码完成了相同的事情:

1
2
3
4
5
6
7
@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();

当断路器回退被调用时,上述示例的请求将会被转发到/inCaseofFailureUseThisURI。需要注意的是,这个示例还演示了(可选)Spring Cloud Netflix Ribbon负载均衡的功能(由目标URI上的lb前缀声明)。

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

示例27:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

在上述示例中,网关应用程序中没有设置回退重点或者处理程序。但是在另外一个应用中注册了一个,注册在地址http://localhost:9994下。

如果请求被转发到了回退上,Spring Cloud CircuitBreaker还提供了引起回退的原因一并转发。它保存在ServerWebExchange.ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR属性中。

对于外部控制器或处理程序场景,可以将异常的详细信息添加至请求头。可以参考FallbackHeaders GatewayFilter Factory获取更多信息。

FallbackHeaders

FallbackHeaders过滤器允许在将请求转发值外部回退处理器或者应用程序的时候,将引起回退的异常信息添加至请求头。

示例28:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

在上述示例中,当断路器运行时发生异常之后,请求将会被转发到http://localhost:9994上配置的回退控制器或者处理程序。回退的异常类型,消息和引起回退的异常根原因(如果有的话)将会被添加到请求头中。

可以通过设置一下参数的值,来覆盖配置中的默认请求头名称(括号中的是默认值):

  • executionExceptionTypeHeaderName (”Execution-Exception-Type”)
  • executionExceptionMessageHeaderName (“Execution-Exception-Message”)
  • rootCauseExceptionTypeHeaderName (“Root-Cause-Exception-Type”)
  • rootCauseExceptionMessageHeaderName (“Root-Cause-Exception-Message”)

MapRequestHeader

此过滤器接收fromHeadertoHeader作为参数。它创建一个新的名为toHeader的请求头并填入fromHeader请求头对应的值。fromHeader不存在对此请求头没有影响。如果toHeader请求头已经存在了,那么它的值将会被增强。下面的示例演示了如何配置MapRequestHeader:

示例29:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: map_request_header_route
uri: https://example.org
filters:
- MapRequestHeader=Blue, X-Request-Red

上面的配置将会在转发下下游的请求头中加上X-Request-Red这个请求头,值则从请求头Blue中获取。

PrefixPath

PrefixPath请求头接收一个prefix参数。

示例30:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath

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

PreserveHostHeader

这个过滤器不接收任何参数。这个过滤器决定请求在转发的时候是否带上原始的主机请求头,而不是由HTTP 客户端确定的请求头。

示例31:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader

RequestRateLimiter

此过滤器使用RateLimiter来决定是否允许当前请求继续执行。如果不允许,则返回HTTP 429-太多请求。

此过滤器接收一个可选的KeyResolver参数和一些针对于Rate limiter的特定参数。

KeyResolver是一个实现了KeyResolver接口的Bean。在配置中,按照SpEL使用名称引用这个Bean。引用一个名为myKeyResolver的表达式为#{@myKeyResolver}。下面就是KeyResolver接口:

1
2
3
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}

KeySolver 接口允许可插拔策略派生用于限制请求的密钥。在将来的里程碑版本中,将有一些密钥解析器实现。

KeySolveer 的默认实现是PrincipalNameKeyResolver,它从ServerWebExchange中获取Principal,并调用Principal.getName()。

默认情况下,如果密钥解析器找不到密钥,请求将被拒绝。可以通过设置 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true或false)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code属性来调整此行为。

Redis RateLimiter

基于Redis的实现是在Stripe的基础上完成的。它要求引入spring-boot-starter-data-redis-reactive启动器。

使用的算法是令牌桶

redis-rate-limiter-replenishRate属性是每秒允许多少个用户请求,不丢弃任何请求。这也是令牌桶填充的速率。

redis-rate-limiter.burstCapacity是允许用户在一秒中之内可以执行的最大请求数。这也是令牌桶能容纳的最大令牌数。将这个属性设置为0,会阻止所有请求。

redis-rate-limiter.requestedTokens属性是每个请求所需要的令牌数。即,每次请求会从令牌桶中取的令牌数。默认值为1。

replenishRateburstCapacity设置为相同的值可以使请求保持在一个稳定的值。将burstCapacty设置为高于replenishRate的值,可以允许临时突发的请求。在这种情况下爱,需要允许rate limiter在突发之间的一段时间(根据配置的replenishRate),因为连续两次突发将导致请求被丢弃(返回HTTP 429,太多请求)。以下的配置配置了一个redis-rate-limiter:

示例34:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
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

下面的代码,配置了一个KeyResolver。

示例35:Config.java

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

这个配置定一个每个用户每秒10个请求的限制,突发情况下,允许20个请求每秒。但是在第二秒中,只有10个请求可用,KeyResolver是获取用户请求参数的简单程序(请注意,不建议用于生产环境)。

还可以将rate limiter定义为实现RateLimiter接口的Bean。在配置中可以使用SpEL按名称引用Bean。myRareLimiter这个Bean在SpEL中就名为#{@myRateLimiter}。一下idngyige使用上一个样例中的KeyResolver的RateLimiter:

示例36:application.yml

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"

RedirectTo

此过滤器接收两个参数statusurlstatus参数应该为3XX系列的重定向HTTP相应码,如301,302.url参数应该是有效的URL。它是Location头的值。对于相对重定向,应该使用no://op作为路由定义的uri。

示例37:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org

上述配置将会对状态码为302,Location头的值为https://acme.org的请求重定向。

RemoveRequestHeader

这个过滤器接收一个name参数。它是待删除的请求头名称。

示例38:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo

它会在将请求转发到下游之前,将请求头X-Request-Foo删除

RemoveResponseHeader

这个过滤器接收一个name参数。它是待删除的响应头名称。

示例39:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Response-Foo

它会在请求返回网关应用之前,将响应中的X-Response-Foo删除。

若要删除任何类型的敏感响应头,可为可能要这样做的任何路由配置此过滤器。此外,还可以通过 spring.cloud.gateway.default配置将此过滤器配置为默认路由,它应用于所有路由。

RemoveRequestParameter

这个过滤器接收一个name参数。它是待删除的查询参数的名称。

示例40:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: removerequestparameter_route
uri: https://example.org
filters:
- RemoveRequestParameter=red

上述配置将从请求的查询参数中,删除red这个参数。

RewritePath

这个过滤器接收一个路径的正则表达式参数regexp和一个replacement代替着参数。它使用Java中的正则表达式来灵活地重写请求路径。

示例41:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/red/**
filters:
- RewritePath=/red(?<segment>/?.*), $\{segment}

对请求路径/red/blue来说,在它被转发到下游之前,它将会被改写为/blue。注意,因为yaml语言规范,$应该被替换为$\

RewriteLocationResponseHeader

该过滤器修改响应头中的Location字段的值。一般是为了屏蔽后端的一些细节。它接收stripVersionMode,locationHeaderName,hostValueprotocolsRegex作为参数。

示例42:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: rewritelocationresponseheader_route
uri: http://example.org
filters:
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,

举个例子,对请求POST api.example.com/some/object/name来说,它的Location响应头的值object-service.prod.example.net/v2/some/object/id将会被重写为api.example.com/some/object/id

stripVersionMode参数有以下取值:

  • NEVER_STRIP:无论如何都不从路径中剥离版本信息
  • AS_IN_REQUEST:和请求中保持一致
  • ALWAYS_STRIP:不管请求什么样,总是剥离版本信息

hostValue参数如果提供了的话,将会被用来替换Location中的host:port部分。如果没有提供,字段Host中的值将会被使用。

protocolsRegex参数一定是一个合法的正则表达式。它用来匹配参数中的协议。如果没有协议能匹配上,这个过滤器就不会产生行为。默认值是:http|https|ftp|ftps

RewriteResponseHeader

这个过滤器接收nameregexpreplacement作为参数。它使用Java的正则表达式来灵活的重写响应头的值。

示例43:application.yml

1
2
3
4
5
6
7
8
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语言规范,$应该被替换为$\

SaveSession

这个过滤器在将请求转发到下游前,会强制执行WebSession::save操作。当Spring Session和数据懒存储结合在一起使用时,尤其有效。需要在进行转发调用之前确保会话状态已经保存。

示例44:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession

如果将 Spring Security 与 Spring 会话集成,并且希望确保安全详细信息已转发到远程进程,则这一点至关重要。

SecureHeaders

按照这篇博客的建议这个过滤器将会添加一系列的请求头至相应中。

下列请求头将会被添加(括号中的为默认值):

  • 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下即可。

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

SetPath

这个过滤器接收一个路径模板参数template。它提供了一种简单的通过允许路径模板化来操作请求路径的简单方法。它使用了Spring框架的URI模板。允许匹配多个段。

示例45:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- SetPath=/{segment}

对于请求/red/blue,在被转发到下游服务之前,它的路径将会被设置为/blue

SetRequestHeader

这个过滤器接收一个name和一个value作为参数。

示例46:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
filters:
- SetRequestHeader=X-Request-Red, Blue

这个过滤器将会使用配置的值,替换所有的重名请求头。

SetRequestHeader 能感知用于匹配路径或主机的 URI 变量。URI 变量可用于该值,并在运行时扩展。下面的示例配置使用变量的 SetRequestHeader 网关筛选器:

示例47:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- SetRequestHeader=foo, bar-{segment}

SetResponseHeader

这个过滤器接收一个name和一个value作为参数。

示例48:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Response-Red, Blue

这个过滤器将会使用配置的值,替换所有的重名请求头。

SetResponseHeader 能感知用于匹配路径或主机的 URI 变量。URI 变量可用于该值,并在运行时扩展。下面的示例配置使用变量的 SetResponseHeader 网关筛选器:

示例49:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- SetResponseHeader=foo, bar-{segment}

SetStatus

这个过滤器接收一个参数status。它必须是一个合法的Spring Http状态码。它可以是整型的404也可以是字符串型的“NOT_FOUND”。

示例50:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: https://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: https://example.org
filters:
- SetStatus=401

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

可以配置此过滤器,从响应头中的代理请求返回原始 HTTP 状态代码。如果配置了以下属性,则响应头将添加到响应中:

示例51:application.yml

1
2
3
4
5
spring:
cloud:
gateway:
set-status:
original-status-header-name: original-http-status

StripPrefix

这个过滤器接收一个参数,parts。参数parts表示要从请求路径中剥离的数目。

示例52:application.yml

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: https://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2

当一个请求通过网关访问/name/blue/red时,它将被剥离成/red

Retry

这个过滤器支持下面的这些参数:

  • retries:重试的次数
  • status:需要被重试的HTTP 状态码。用org.springframeword.http.HttpStatus表示
  • methods:需要被重试的方法。用org.springframeword.http.HttpMethdo表示。
  • series:需要被重试的一系列HTTP状态码。用org.springframeword.http.HttpStatus.Series表示
  • exceptions:需要被重试的异常
  • backoff:重试退火的时间。退火的间隔计算公式为firstBackoff * (factor ^ n)。其中n是第n次重试。如果配置了maxBackoff,重试最大间隔时间就不能超过maxBackoff。如果basedOnPreviousValue被设置为true,那么回退的计算公式就变为prevBackoff * factor

它们的默认值分别是:

  • retries:3次
  • methods:GET方法
  • series:5XX系列的错误码
  • exceptions:IOException和TimeoutException
  • backoff:disabled

示例53:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

RequestSize

当请求大小大于允许的限制时,此过滤器可以限制请求到达下游服务。过滤器接收maxSize 参数。maxSize 是”DataSize 类型”,因此值可以定义为数字,后跟可选的 DataUnit 后缀,如”KB”或”MB”。字节的默认值为”B”。它是以字节为单位定义的请求的允许大小限制。以下列表配置了请求大小网关筛选器:

示例54:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000

将响应状态设置为 413 ,当请求因大小而被拒绝时,则出现额外的请求头错误消息。下面的示例显示这样的错误消息:

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

SetRequestHost

在某些情况下,需要重写主机的请求头。这个过滤器可以用指定的值替换现有的Host请求头。这个过滤器接收一个host参数。

示例55:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
gateway:
routes:
- id: set_request_host_header_route
uri: http://localhost:8080/headers
predicates:
- Path=/headers
filters:
- name: SetRequestHost
args:
host: example.org

此过滤器将会用example.org替换host请求中的值。

ModifyRequestBody

这个过滤器允许在请求转发至下游服务之前,修改它的请求体。

注意:这个过滤器只能在Java代码中配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@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;
}
}

ModifyResponseBody

这个过滤器允许在响应回到网关应用之前,修改它的响应体。

注意:这个过滤器只能在Java代码中配置。

1
2
3
4
5
6
7
8
9
@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();
}

Default Filters

可以通过配置spring.cloud.gateway.default-filters,将一系列的过滤器配置为默认过滤器,以便将它们应用到所有的路由中。

示例54:application.yml

1
2
3
4
5
6
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin

全局过滤器

全局过滤器和Gateway过滤器一样具有同样的接口。它们是有条件的应用于所有路由的过滤器。

结合全局过滤器和GatewayFilter Ordering

当请求与路由匹配的时候,filtering Web Handler将会把所有的全局过滤器和所有特定于路由的网关过滤器添加到过滤器链中。它们之间通过org.springframework.core.Order接口来实现排序。可以通过实现Order接口的getOrder方法,来自定义过滤器的优先级。

由于Spring Cloud 网管区分“前置”和“后置”过滤器,因此,优先级最高的“前置”过滤器是第一个执行,在“后置”过滤器中是最后一个执行。

下面的例子,配置了一个过滤器链:

示例57:ExampleConfiguration.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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;
}
}

Forward Routing

ForwardRoutingFilter从exchange的属性ServerWebExchangeUtils.GATEWAY_REQUEST_URI_ATTR中提取参数。如果这个URI符合forward模式(forward://myservice),它就使用Spring框架的DispatcherHandler来处理请求。请求URL的路径部分将会被forward中的路径部分覆盖。原始的请求头将会被追加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中。

LoadBalancerClient

这个过滤器从ServerWebExchangeUtils.GATEWAY_REQUEST_URI_ATTR属性中提取参数。如果URL符合lb模式(lb://myservice),那么它将会使用Spring Cloud 的LoadBalcnerClient将服务名称(myService)解析为实际的主机和端口,并替换同一属性中的URI。未修改的原始URL将附加到ServerWebExchangeUtils.GATEWAY_ORIGIN_REQUEST_URL_ATTR属性中。这个过滤器确认属性ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR的值是否为lb,是的话,则使用相同的规则。

示例58:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**

默认情况下,如果LoadBalancer无法找到服务的示例,会返回一个503.可以通过配置spring.cloud.gateway.loadbalancer.use404=true将网关的返回修改为404.

从服务实例返回的isSecure属性将覆盖向网管发出的请求中指定的值。比如,如果请求通过HTTPS进入网关,但是服务实例认为它不安全,那么就通过HTTP协议转发请求到下游,反之亦然。但是,GATEWAY_SCHEME_PREFIX_ATTR属性为网关配置中的路由指定了一个策略,即删除前缀,并且路由 URL 中生成的方案将覆盖 ServiceInstance 配置。

LoadBalancerClient过滤器使用了一个阻塞式的Ribbon LoadBalancerClient。建议使用ReactiveLoadBalancerClient过滤器。可以通过设置spring.cloud.loadbalancer.ribbon的值为false,切换到ReactiveLoadBalancerClient过滤器。

ReactiveLoadBalancerClient

这个过滤器从ServerWebExchangeUtils.GATEWAY_REQUEST_URI_ATTR属性中提取参数。如果URL符合lb模式(lb://myservice),那么它将会使用Spring Cloud 的ReactorLoadBalcnerClient将服务名称(myService)解析为实际的主机和端口,并替换同一属性中的URI。未修改的原始URL将附加到ServerWebExchangeUtils.GATEWAY_ORIGIN_REQUEST_URL_ATTR属性中。这个过滤器确认属性ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR的值是否为lb,是的话,则使用相同的规则。

示例59:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**

默认情况下,如果LoadBalancer无法找到服务的示例,会返回一个503.可以通过配置spring.cloud.gateway.loadbalancer.use404=true将网关的返回修改为404.

从服务实例返回的isSecure属性将覆盖向网管发出的请求中指定的值。比如,如果请求通过HTTPS进入网关,但是服务实例认为它不安全,那么就通过HTTP协议转发请求到下游,反之亦然。但是,GATEWAY_SCHEME_PREFIX_ATTR属性为网关配置中的路由指定了一个策略,即删除前缀,并且路由 URL 中生成的方案将覆盖 ServiceInstance 配置。

Netty Routing

如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR属性是以http或者https开头,它将会运行当前过滤器。它使用Netty HttpClient向下游发送代理请求。响应被放在ServerWebExchangeUtils.CLIENT_RESPINSE_ATTR属性中供后续的过滤器使用。(有个名为WebClientHttpRoutingFilter的过滤器实现相同的功能,但是不需要Netty)。

Netty Write Response

如果属性ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR存在一个Netty HttpClientResponse,那么当前过滤器将会执行。它在所有其他过滤器完成并将代理响应写入网关客户端响应后运行。(还有一个实验性的 WebClientWrite 响应过滤器执行相同的功能,但不需要 Netty。

RouteToRequestUrl

如果ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR存在一个路由对象,当前请求将运行。它基于请求的URI创建一个新的URI,但使用路由对象的URI属性进行更新。新的URI被放置在ServerWebExchangeeUtils.GATEWAY_REQUEST_URL_ATTR属性中。

如果URI具有前缀,例如lb:ws://serviceid,前缀lb将从url中被剥离,并放置在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR属性中。供后续的过滤器使用。

Websocket Routing

如果属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR中的URI具有ws或者wss前缀,当前过滤器将会运行。它使用Spring WebSocket来将websocket请求转发到下游服务。

可以配置URI为lb:ws://serviceid来达到负载均衡的目的。

示例60:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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/**

Gateway Metrics

在项目中添加spring-boot-starter-actuator依赖可以开启gateway metrixs。接着,只要spring.cloud.gateway.metrics.enabled属性没有被设置为false,这个过滤器就会执行。这个过滤器会添加一个具有以下标签的计时器指标:

  • routeId:路由id
  • routerUri:被路由的API
  • outcome:结果,通过HttpStatus.Series分类
  • status:返回客户端的HTTP状态
  • httpStatusCode:返回客户端的HTTP状态码
  • httpMethod:请求的HTTP方法

这些指标可以通过/actuator/metrics/gateway.requests获得。并且可以轻易的和普罗米修斯结合来创建一个Gragana dashboard

标记一个Exchange为已路由

网关路由了ServerWebExchange之后,它会将gatewayAlreadyRouted作为属性添加到该exchange中。一旦一个请求被标记为已经路由,其他的路由过滤器将不会再路由这个请求,实际上是跳过该过滤器。可以使用一些简便的方法将exchange标记为已路由或者检查exchange是否已被路由。

  • ServerWebExchangeUtils.isAlreadyRouted接收一个ServerWebExchange对象并检查其是否已经被路由
  • ServerWebExchangeUtils.setAlreadyRouted接收一个ServerWebExchange对象并将其标记为”已路由“。

HttpHeaders过滤器

HttpHeaders过滤器应用于向下游发送请求之前。

Forwarded Headers

此过滤器创建一个Forwarded请求头来发送到下游服务。它将当前请求的Host头,scheme和端口添加到任何现有的Forwarded头中。

RemoveHopByHopHeaders

该过滤器可以同转发的请求中删除指定的请求头。默认被删除的请求头列表来自IETF

默认删除的请求头为:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer=Encoding
  • Upgrade

可以通过配置属性spring.cloud.gateway.filter.remove-non-proxy-headers.headers来改变待删除的请求头列表。

XForwarded

XForwarded过滤器在把请求发送到下游服务之前,会创建各种X-Forwarded-×请求头。它使用当前请求的Host,scheme(?),端口和路径创建各种请求头。

可以通过下列参数来控制不同的请求头的产生(默认为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

TLS和SSL

Spring Cloud Gateway可以按照通常的Spring Server配置来监听HTTPS的请求。参见如下示例:

示例61:application.yml

1
2
3
4
5
6
7
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

1
2
3
4
5
6
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true

生产环境不适合使用不安全的信任管理器。对于生产部署,可以使用以下配置使网管信任下游的一组证书:

示例63:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem

如果Spring Cloud gateway没有配置授信的证书,将会使用默认的信任存储(可以通过覆盖javax.net.ssl.trustStore属性来覆盖默认配置)。

TLS 握手

网关维护了一个客户端池用来路由到后端。当通过HTTPS进行通信时,客户端会初始化一个TLS握手,顺带一系列的超时配置。可以通过以下配置配置这些超时:

示例64:application.yml

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
httpclient:
ssl:
handshake-timeout-millis: 10000
close-notify-flush-timeout-millis: 3000
close-notify-read-timeout-millis: 0

配置

Spring Cloud Gateway由一系列的RouteDefinitionLocator实例驱动。

示例65:application.yml

1
2
3
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}

默认情况下,PropertiesRouteDefinitionLocator通过Spring Boot的@ConfigurationPtoperties机制加载配置。

前面的配置示例都使用位置参数而不是命名参数的快捷方式表示法。以下两个示例等效:

示例66:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

对于网管的一些使用场景,属性文件已经足够了。但某些生产场景下,受益于从外部加载配置(例如数据库)。未来的里程碑版本中,RouteDefinitionLocator将基于Spring Data Repositories实现(比如Redis,MongoDB和Cassandra)。

路由元数据配置

可以通过元数据为每个路由配置一些额外的参数。

示例67 application.yml

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
routes:
- id: route_with_metadata
uri: https://example.org
metadata:
optionName: "OptionValue"
compositeObject:
name: "value"
iAmNumber: 1

接着就可以从exchange中获取这些元数据属性:

1
2
3
4
5
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// get all metadata properties
route.getMetadata();
// get a single metadata property
route.getMetadata(someKey);

Http 超时配置

可以针对所有的也可以针对特定的路由配置Http超时(响应和连接)。

全局超时

如果要配置全局超时:

connect-timeout需要以毫秒为单位配置

response-time需要以java.time.Duration为单位配置。

全局HTTP超时配置

1
2
3
4
5
6
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s

单路由超时

如果要针对特定的路由配置超时

connect-timeout需要以毫秒为单位配置

response-timeout需要以毫秒为单位配置。

单路由超时配置-配置文件

1
2
3
4
5
6
7
8
9
id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200

单路由超时配置-Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
}

流畅的Java路由API

为了允许在Java中进行简单的配置,RouteLocatorBuilder Bean包含了一个流畅的API。下面的例子显示了它是如何工作的:

示例68:GatewaySampleApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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定义的断言通过逻辑 ‘和’组合在一起。借助使用Java API,可以在断言上使用and(),or()和negate()运算符。

DiscoveryClient Route Definition Locator

可以根据DiscoveryClient兼容的服务注册中心来为网关创建路由。为此,将配置spring.cloud.gateway.discovery.locator.enable设置为true,并确保类路径上存在一个DiscoveryClient的实现(例如,Netflix Eureka,Consul或者Zookeeper)。

为DiscoverClient路由配置断言和过滤器

默认情况下,网关会为DiscoveryClient创建的路由定义单个断言和过滤器。

默认的断言是一个路径断言。它的模式是/serviceId/*,其中serviceId是DiscoveryClient中的服务ID。

默认的过滤器是一个重写路径过滤器。它会将serviceId/(?<remaining>.*)中的部分,替换为/${remaining}。这个过滤器将会在请求发送至下游的服务之前将服务ID剥离。

如果想要自定义DiscoveryClient使用的断言或者过滤器,设置spring.cloud.gateway.discovery.locator.predicates[x]spring.cloud.gateway.discovery.locator.filters[y]。如果需要保留上出的默认功能,确保包含了默认的断言和过滤器。

示例69:application.yml

1
2
3
4
5
6
7
8
9
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: Hystrix
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}'"

Reactor Netty Access Logs

配置-Dreactor.netty.http.server.accessLogEnabled=true为true,可以打开Reactor Netty access logs.可以配置日志系统处理一个单独的日志文件。

示例70:application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
<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>

CORS配置

可以配置网关来控制CORS行为。“全局”CORS配置是一个指向Spring 框架的URL映射表。

示例71:application.yml

1
2
3
4
5
6
7
8
9
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET

在上一个示例中,来自docs.spring.io的GET请求都被允许放行。

若要为某些网关路由谓词未处理的请求提供相同的 CORS 配置,可以将属性spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping设置为true。当场是支持CORS预检请求并且因为方法是OPTIONS导致路由断言匹配失败是,非常有用。

Actuator API

/gateway执行器允许监控Spring Cloug Gateway并且与之交互。要想远程访问,必须启动端点,并将其通过HTTP或者JMX对外暴露。

示例72:application.yml

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

Verbose Actuator Format

一种新的,更详细的格式已经到Spring Cloud Gateway中。它向每个路由添加更多详细信息,允许查看与每个路由关联的断言和过滤器以及任何可用的配置。下面的示例配置 /actutor/gateway/routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
[
{
"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.yml

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

这个在将来的发行版中,会默认设置为true。

检索路由过滤器

全局过滤器

向地址/actuator/gateway/globalfilters发送一个get请求,就能检索到应用于所有路由的全局过滤器。接口的响应和下面的类似:

1
2
3
4
5
6
7
8
9
10
{
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5": 10100,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646
}

响应中包括每个全局路由器的详情。对每个全局路由器,返回一个该路由器的String表示法和它在过滤器链中的顺序。

路由过滤器

向接口/actuator/gateway/routerfilters发送一个get请求,就ikeyi减速到应用于路由的网管过滤器工厂。响应和下面的类似:

1
2
3
4
5
{
"[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
"[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}

响应中包含任何应用于特定路由器的网关过滤器工厂的详细信息。对于每个工厂,都有相应对象的字符串表示形式(例如,[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object])。请注意,null 值是由于终结点控制器的未完成实现造成的,因为它尝试设置筛选器链中对象的顺序,这不适用于 GatewayFilter 工厂对象。

刷新路由缓存

发起一个POST /actuator/gateway/refresh请求,就能刷新路由缓存。成功发起这个请求得到的返回为空,返回码为200。

检索网关路由

发起一个GET /actuator/gateway/routes请求,就inenghuoqu到网关中定义的路由信息。返回的结果类似于如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[{
"route_id": "first_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@1e9d7e7d",
"filters": [
"OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/674480275@6631ef72, order=0}"
]
},
"order": 0
},
{
"route_id": "second_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@cd8d298",
"filters": []
},
"order": 0
}]

响应中包好了所有网关中所有路由的详细信息。下表描述了返回中的每个字段的含义:

路径 类型 描述
route_id String 路由ID
route_object.predicate Object 路由断言
route_object.filters Array 网关的网管路由器列表
order Number 路由的顺序

获取特定路由的信息

发起一个GET /actuator/gateway/routes/{id}请求来获取指定路由的详细信息。返回结果类似于下文:

1
2
3
4
5
6
7
8
9
10
{
"id": "first_route",
"predicates": [{
"name": "Path",
"args": {"_genkey_0":"/first"}
}],
"filters": [],
"uri": "https://www.uri-destination.org",
"order": 0
}]

字段释义见下表:

路径 类别 描述
id String 路由id
predicates Array 路由断言列表。数组中的每项包括了断言的名称和参数
filters Array 指定路由的过滤器集合
uri String 路由的目的URI
order Number 路由的顺序

创建和删除指定的路由

创建路由:POST /gateway/routes/{route_id_to_create},请求体参考这里

删除路由:DELETE /gateway/routes/{route_id_to_delete}

回顾:所有终结点的列表

下表描述了Spring Cloud Gateway的所有端点(剥离了前缀/actuator/gateway):

ID HTTP方法 描述
globalfilters GET 获取路由的所有全局过滤器
routefilters GET 获取指定路由的所有网关过滤器工厂
refresh POST 清理路由缓存
routes GET 获取网关中定义的路由列表
routes/{id} GET 获取指定路由的详情
routes/{id} POST 创建一个新路由
routes/{id} DELETE 删除指定的路由

问题定位

日志级别

以下的日志在DEBUG和TRACE级别可能包含一些有价值的故障排除信息:

  • org.springframework.cloud.gateway
  • org.springframework.http.server.reactive
  • org.springframework.web.reactive
  • org.springframework.boot.autoconfigure.web
  • reactor.netty
  • redisratelimiter

”窃听“

反应式Netty HttpClient和HttpServer可以启用”窃听“。当reactor.netty 日志级别设置为DEBUG或TRACE时,它记录一些通过网络发送和接受的请求头和请求体。要启用”窃听“,要设置set spring.cloud.gateway.httpserver.wiretap=true或者对HttpServer和HttpClient来书,设置spring.cloud.gateway.httpclient.wiretap=true

开发者指南

编写自定义路由断言工厂

自定义路由断言工厂需要实现RoutePredicateFactory类。有一个名为AbstractRoutePredicateFactory的抽象类可以被扩展。

MyRoutePredicateFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.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
}

自定义网关过滤器工厂

要自定义一个网关过滤器工厂,可以实现GatewayFilterFactory接口,或者扩展抽象类AbstractGatewayFilterFactory

示例74:PreGatewayFilterFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
}

在配置中命名自定义过滤器和引用

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

例如,如果配置中要引用一个名为Something的过滤器,那么这个过滤器的类名一定要是SomethingGatewayFilterFactory

自定义全局过滤器

必须实现GlobalFilter接口。这个过滤器会应用到所有的请求上。

下面的样例展示了如何设置全局”前置“和”后置“过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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();
}

使用Spring MVC或者Webflux构件一个简单的网关

Spring Cloud Gateway提供了一个使用的对象,称之为ProxyExchange。可以在常规的Spring Web Handler中使用它作为方法参数。它支持通过镜像HTTP谓词的方法同下游进行HTTP交换。使用MVC的话,它还支持通过forward()方法转发到本地的handler上。要使用代理交换,请将正确的模块包括在类路径中(spring-cloud-gateway-mvc 或 spring-Cloud-gateway-webflux)。

下面的MVC例子代理了请求/test到下游的远端服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
@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完成了同样的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
@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提供的简便方法是Handler方法可以发现和增强传入请求的URI路径。例如,你可能希望提取路径的末尾元素,以便将他们传递到下游:@GetMapping("/proxy/path/**")

1
2
3
4
5
@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 的所有功能都可用于网关Handler方法。因此,可以注入请求头和查询参数,例如,还可以使用映射注释中的声明约束传入请求。更多关于功能@RequestMapping,请参阅 Spring MVC 中有关文档。

可以通过ProxyExchange的header方法,将请求头添加到下游的响应上。

还可以通过添加一个映射器到get方法(或者其他方法)来操作响应的请求头(或者响应中的其他属性)。这个映射器是一个函数,它使用传入请求的ResponseEntity作为参数,并将它转换为传出响应。

”敏感“的请求头(默认情况下,cookie和鉴权)和代理请求头(xforwarded-×)是享受最高等的支持的。

配置属性

参见附录

0%