微服务网关Spring Cloud Gateway之Filter
在 Spring Cloud Gateway
中,除断言外,Filter
(过滤器)也是一个重要知识点。
提起过滤器,做过 Java
开发的读者应该不会陌生,Spring Cloud Gateway
网关中的过滤器也如此,可以在网关路由到具体的微服务请求之前或网关收到具体的微服务响应之后,为请求对象和响应对象添加一些自定义的编码。
在实际项目的开发过程中,使用过滤器的场景要比使用断言工厂的场景多一些。Spring Cloud Gateway
内置的断言工厂中最常用的其实是路径判断,即 PathRoutePredicateFactory
。而过滤器的适用场景和功能比断言的适用场景和功能多一些,如路由规则匹配了,但是还需要对 Request
对象或 Response
对象做额外的定制操作,就可以把过滤器搬过来。与断言工厂不同的一点是,断言工厂只负责判断并返回一个布尔值,并没有额外的操作,而在过滤器中可以直接修改 Request
对象和 Response
对象,如果在进入过滤器后某些请求依然不符合规则,就可以直接自定义响应内容。
Spring Cloud Gateway
根据作用范围可将过滤器分为 GatewayFilter
和 GlobalFilter
,通俗一点理解就是局部过滤器和全局过滤器。其中,局部过滤器只对某一个路由配置生效,而全局过滤器作用于所有路由配置。Spring Cloud Gateway
网关中的这两类过滤器都支持开发人员自定义操作,除 Spring Cloud Gateway
内置的过滤器外,也可以自行添加过滤器来实现一些特殊的需求。
Spring Cloud Gateway的内置过滤器
内置过滤器列表及功能
Spring Cloud Gateway
组件中内置的局部过滤器都实现了 AbstractGatewayFilterFactory
抽象类,在 3.1.1 版本中共有 30 多个,部分内置的局部过滤器列表如图 9-12 所示。

常见的内置过滤器介绍如下。
-
StripPrefixGatewayFilterFactory
:该过滤器接收一个parts
参数,parts
参数表示在将请求发送到微服务实例之前要从请求 URL 中剥离的路径数量。比如,发送的请求路径为/newbee-ltd/manage/goods/save
,如果配置参数为 1,则实际路由到微服务实例的URL
为/manage/goods/save
;如果配置参数为 2,则实际路由到微服务实例的URL
为/goods/save
。配置格式如下:- StripPrefix=2
-
SetStatusGatewayFilterFactory
:该过滤器接收一个status
参数,修改Response
对象的HTTP
状态码。status
参数必须是有效的状态码,可以用整数值404
或枚举的字符串NOT_FOUND
表示。配置格式如下:- SetStatus=404
-
AddRequestHeaderGatewayFilterFactory
:该过滤器会给当前的Request
对象添加一个Header
参数及参数值。配置格式如下:- AddRequestHeader=os,HarmonyOS
-
AddRequestParameterGatewayFilterFactory
:该过滤器会给当前的Request
对象添加一个请求参数及参数值。配置格式如下:- AddRequestParameter=name,newbee-mall-cloud
-
AddResponseHeaderGatewayFilterFactory
:该过滤器会给当前的Response
对象添加一个Header
参数及参数值。配置格式如下:- AddResponseHeader=token,newbee*
-
PrefixPathGatewayFilterFactory
:该过滤器接收一个Prefix
参数,Prefix
参数表示在将请求发送到微服务实例之前要在请求URL
中添加的路径。配置格式如下:- PrefixPath=/newbee-cloud
比如,发送的请求路径为 /goods/save
,那么实际路由到微服务实例的 URL
为 /newbee-cloud/goods/save
。
-
PreserveHostGatewayFilterFactory
:此过滤器无须配置参数值,主要用于确定是否发送原始主机头,而不是由HTTP
客户端确定主机头。配置格式如下:- PreserveHost
-
RedirectToGatewayFilterFactory
:该过滤器接收两个参数status
和url
。status
参数应为300
系列的HTTP
状态码,如301
、302
。url
参数应为有效URL
地址。配置格式如下:- RedirectTo=302,https://juejin.cn
Spring Cloud Gateway
组件中内置的全局过滤器都实现了 GlobalFilter
接口,在 3.1.1 版本中共有十几个,列表如图 9-13 所示。

全局过滤器功能非常全面,包括请求的基本处理、负载均衡功能、响应结果处理、监控等。感兴趣的读者可以阅读这些类的源码,逻辑并不复杂。代码量比较多的是 ReactiveLoadBalancerClientFilter
过滤器,其实看到这个类的名称,读者应该很熟悉,在前面章节中介绍过 LoadBalancerClient
这个类,它们非常相似,该过滤器的功能是结合服务发现机制进行负载均衡操作。
Spring Cloud Gateway
中内置的过滤器较多,功能也非常丰富,更多内容可以参考 Spring Cloud Gateway
的官方文档,见网址6。
使用内置的局部过滤器配置路由规则
Spring Cloud Gateway
内置过滤器介绍完毕,接下来笔者使用内置的 StripPrefixGatewayFilterFactory
和 RedirectToGatewayFilterFactory
编写一个示例演示它们的作用。本节代码是在 spring-cloud-alibaba-gateway-demo
项目的基础上修改的,具体步骤如下。
-
修改项目名称和基本配置。
修改项目名称为
spring-cloud-alibaba-gateway-filter-demo
,之后把各个模块中pom.xml
文件的artifactId
修改为spring-cloud-alibaba-gateway-filter-demo
。为了做章节区分,这里把gateway-demo
项目中的端口号修改为8147
,并且在gateway-demo
项目配置文件中添加注册中心的配置。 -
使用内置过滤器。
除
Path
路径规则外,分别增加参数规则和请求方法的规则,代码如下:spring.cloud.gateway.routes[0].id=goods-service-route spring.cloud.gateway.routes[0].uri=lb://newbee-cloud-goods-service spring.cloud.gateway.routes[0].order=1 spring.cloud.gateway.routes[0].predicates[0]=Path=/newbee-cloud/goods/** ## 访问/newbee-cloud/goods 开头的请求,都会被设置为/goods 开头的请求 spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1 spring.cloud.gateway.routes[1].id=shopcart-service-route spring.cloud.gateway.routes[1].uri=lb://newbee-cloud-shopcart-service spring.cloud.gateway.routes[1].order=1 spring.cloud.gateway.routes[1].predicates[0]=Path=/shop-cart/** ## 访问/shop-cart 开头的请求,都会被重定向到掘金官网 spring.cloud.gateway.routes[1].filters[0]=RedirectTo=302,https://juejin.cn
在原有路由配置的基础上,分别给
goods-service-route
路由和shopcart-service-route
路由新增一条过滤器配置。访问/newbee-cloud/goods
开头的请求,都会被设置为/goods
开头的请求。访问/shop-cart
开头的请求,都会被重定向到掘金官网。编码完成后进行功能验证,需要启动
Nacos Server
,之后依次启动gateway-demo
、goods-service-demo
和shopcart-service-demo
项目。如果未能成功启动,则开发人员需要查看控制台中的日志是否报错,并及时确认问题和修复。启动成功后进入Nacos
控制台,单击 “服务管理” 中的服务列表,可以看到列表中已经存在三个服务的服务信息。依次使用不同的地址进行测试,结果见表 9-4。
Figure 3. 表9-4 使用不同的地址进行测试的结果这里重点解释一下为什么第 1 条请求和第 2 条请求的结果不同。
首先,网关项目的端口号为
8147
,商品服务项目的端口号为8140
。商品详情接口是在商品服务中的,网关项目中并没有这个接口,如果想通过网关访问这个接口,就必须路由到商品服务。那么,访问 http://localhost:8147/goods?goodsId=2035 这个链接时,请求的是网关项目,而网关项目中的路由配置无法匹配
/goods
开头的这个链接。因为路由配置中到商品服务的Path
断言为Path=/newbee-cloud/goods/**
,无法匹配,直接报错 404。其次,在访问 http://localhost:8147/newbee-cloud/goods?goodsId=2035 这个链接时,因为被网关中的路由配置匹配到了,所以会路由到商品服务。此时读者可能会有疑问,商品服务中并没有处理
/newbee-cloud/goods
路径的接口,为什么结果是正常的呢?因为增加了一个StripPrefix=1
的配置,在请求路由到商品服务之前,这个请求地址已经由/newbee-cloud/goods?goodsId=2035
变成了/goods?goodsId=2035
,所以能够获取正确的接口数据,即过滤器生效。
还有其他内置的局部过滤器可供开发人员使用,因篇幅有限,就不再一一举例了,读者可以自行对照前文中介绍的内容进行编码和测试。
自定义网关过滤器
自定义局部过滤器编码实践
与自定义断言工厂一样,Spring Cloud Gateway
同样支持开发人员自定义过滤器。前文中介绍的内置断言工厂都实现了 AbstractGatewayFilterFactory
抽象类,命名方式为 xxxGatewayFilterFactory
,在配置文件中写上 -xxx
即可。根据这几个固定的写法,可以自行实现一个局部过滤器类。与前文中自定义断言工厂一样,这里实现一个局部过滤器只允许查询 goodsId
为 10000~100000
的商品数据,以下为具体的实现步骤。
-
编写
GoodsIdGatewayFilterFactory
类。在
gateway-demo
项目中,新建ltd.gateway.cloud.newbee.filter
包,并新建GoodsIdGatewayFilterFactory.java
文件,具体代码及注释如下:package ltd.gateway.cloud.newbee.filter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFa ctory; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @Component public class GoodsIdGatewayFilterFactory extends AbstractGatewayFilterFactory<GoodsIdGatewayFilterFactory.Config> { public GoodsIdGatewayFilterFactory() { //构造函数 super(Config.class); } @Override public List<String> shortcutFieldOrder() { // 定义配置文件中的参数项(最小值和最大值) return Arrays.asList("minValue", "maxValue"); } @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取参数值 String goodsIdParam = exchange.getRequest().getQueryParams().getFirst("goodsId"); // 判空 if (!StringUtils.isEmpty(goodsIdParam)) { int goodsId = Integer.parseInt(goodsIdParam); // 判断goodsId是否在配置区间内,直接放行 if (goodsId > config.getMinValue() && goodsId < config.getMaxValue()) { return chain.filter(exchange); } else { // 不符合条件,返回错误的提示信息,不进行后续的路由 byte[] bytes = ("BAD REQUEST").getBytes(StandardCharsets.UTF_8); DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(bytes); exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); return exchange.getResponse().writeWith(Flux.just(wrap)); } } // 直接放行 return chain.filter(exchange); } }; } //接收配置文件中定义的最大值和最小值 public static class Config { private int minValue; private int maxValue; public int getMinValue() { return minValue; } public void setMinValue(int minValue) { this.minValue = minValue; } public int getMaxValue() { return maxValue; } public void setMaxValue(int maxValue) { this.maxValue = maxValue; } } }
在该类中,分别定义了配置文件中定义的区间参数
minValue
和maxValue
。在filter()
方法中定义了具体的判断逻辑,获取goodsId
参数值后判断是否在配置的区间内,若符合条件,则直接放行请求;若不符合条件,则返回错误的提示信息,不进行后续的路由。 -
配置自定义局部过滤器
在
application.properties
配置文件中配置自定义的断言工厂。路由配置如下:spring.cloud.gateway.routes[0].id=goods-service-route spring.cloud.gateway.routes[0].uri=lb://newbee-cloud-goods-service spring.cloud.gateway.routes[0].order=1 spring.cloud.gateway.routes[0].predicates[0]=Path=/goods/** # 自定义过滤器配置,配置项为 goodsId,最大值为 100000,最小值为 10000 spring.cloud.gateway.routes[0].filters[0]=GoodsId=10000,100000
-
功能验证。
编码完成后,需要启动
Nacos Server
,之后依次启动gateway-demo
和goods-service-demo
项目。如果未能成功启动,则开发人员需要查看控制台中的日志是否报错,并及时确认问题和修复。启动成功后进入Nacos
控制台,单击 “服务管理” 中的服务列表,可以看到列表中已经存在两个服务的服务信息。 -
打开浏览器进行功能验证,依次使用不同的地址进行测试,页面显示内容如图 9-14 所示,结果整理见表 9-5。
Figure 4. 图9-14 使用GoodsIdGatewayFilterFactory局部过滤器后的请求测试结果Figure 5. 表9-5 使用不同的地址进行测试的结果
所有请求都被自定义过滤器处理,并且最终结果与预期结果一致。自定义局部过滤器验证成功!
自定义全局过滤器编码实践
在 Spring Cloud Gateway
组件中,全局过滤器同样支持开发人员的自定义操作。编码实现也不复杂,只需要实现 GlobalFilter
、Ordered
这两个接口即可。过滤器的命名没有限定的规则,比较自由。接下来,笔者将实现一个全局过滤器,用于统计每个请求的处理时间。之后重启项目访问各个URL即可在控制台看到接口调用时间。具体操作步骤如下。
-
编写
TimeCalculateGlobalFilter
类。在
gateway-demo
项目的td.gateway.cloud.newbee.filter
包中新建TimeCalculateGlobalFilter.java
文件,具体代码及注释如下:package ltd.gateway.cloud.newbee.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; //全局过滤器,统计接口调用时间 @Component public class TimeCalculateGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 请求开始时间 long startTime = System.currentTimeMillis(); String requestURL = String.format("Host:%s Path:%s Params:%s", exchange.getRequest().getURI().getHost(), exchange.getRequest().getURI().getPath(), exchange.getRequest().getQueryParams()); System.out.println(requestURL); return chain.filter(exchange).then(Mono.fromRunnable(() -> { // 请求结束时间 long endTime = System.currentTimeMillis(); // 打印调用时间 long requestTime = endTime - startTime; System.out.println(exchange.getRequest().getURI().getPath() + "请求时间为" + requestTime + "毫秒"); })); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
该类中的逻辑比较简单,先获取请求开始的时间,然后等待具体微服务的响应,并记录请求结束时间,最后计算调用时间并输出到控制台。
-
功能验证
此时,
gateway-demo
项目的配置文件如下:server.port=8147 # 应用名称 spring.application.name=newbee-cloud-gateway-service # 注册中心 Nacos 的访问地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 # 登录名(默认为 nacos, 可自行修改) spring.cloud.nacos.username=nacos # 密码(默认为 nacos, 可自行修改) spring.cloud.nacos.password=nacos spring.cloud.gateway.routes[0].id=goods-service-route spring.cloud.gateway.routes[0].uri=lb://newbee-cloud-goods-service spring.cloud.gateway.routes[0].order=1 spring.cloud.gateway.routes[0].predicates[0]=Path=/goods/** spring.cloud.gateway.routes[1].id=shopcart-service-route spring.cloud.gateway.routes[1].uri=lb://newbee-cloud-shopcart-service spring.cloud.gateway.routes[1].order=1 spring.cloud.gateway.routes[1].predicates[0]=Path=/shop-cart/**
全局过滤器不用在配置文件中做额外的配置,所有请求都会使用它。
编码完成后,需要启动
Nacos Server
,之后依次启动gateway-demo
、goods-service-demo
和shopcart-service-demo
这三个项目。如果未能成功启动,则开发人员需要查看控制台中的日志是否报错,并及时确认问题和修复。启动成功后进入Nacos
控制台,单击 “服务管理” 中的服务列表,可以看到列表中已经存在三个服务的服务信息。 -
打开浏览器进行功能验证,依次使用不同的地址进行测试,结果整理见表9-6。
Figure 6. 表9-6 使用不同的地址进行测试的结果所有请求都被自定义的
TimeCalculateGlobalFilter
过滤器处理,并且每个请求所花费的时间都被计算并打印出来。自定义全局过滤器功能验证成功!