8-SpringCloud-服务网关 Gateway-高级特性Predicate
1.6.2.5 Gateway 高级特性之 Predicate 断言
Spring Cloud Gateway 包含许多内置的 Route Predicate 工厂,这些 Predicate 匹配 HTTP 请求的不同属性,多个 Route Predicate 工厂可通过逻辑 and 进行组合。具体信息可以参考官方网址https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/request-predicates-factories.html。
总之,Predicate 就是实现一组路由匹配规则,让请求找到对应的 Route 进行处理。


启动微服务9527,查看后台输出,输出信息如下所示。

这些 Route Predicate 工厂的整体架构如下图所示。

1.6.2.5.1 常用的内置 Route Predicate
Predicate 配置语法总体概述,Predicate 配置语法分为2种,分别是 shortcuts 和 fully expanded arguments ,但大多数情况都采用 shortcuts 配置方法。具体信息可参考官方网址https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/configuring-route-predicate-factories-and-filter-factories.html。

-
配置方法1,shortcuts,在配置文件 application.yml 中可添加如下代码。

-
配置方法2,fully expanded arguments,在配置文件 application.yml 文件中添加如下代码。

如下的 Route Predicate 工厂配置的测试网址都是http://localhost:9527/pay/gateway/get/1。
1. After Route Predicate
这个 After Route Predicate 工厂携带一个时区参数 ZonedDateTime,用于匹配请求的携带参数。只有在此时区参数之后才能访问服务,否则访问不成功。

首先创建一个生成 ZonedDateTimeDemo 的类,随便创建在任意的一个包内,这里创建在 cloud-apis-commons 模块的 com.atguigu.test 包中。具体代码如下所示。
package com.atguigu.test;import java.time.ZonedDateTime;public class ZonedDateTimeDemo {public static void main(String[] args) {ZonedDateTime zbj= ZonedDateTime.now();System.out.println(zbj);}
}
然后在配置文件 application.yml 中输入如下配置,配置 After Route Predicate 工厂。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由- After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
在上述配置文件的时区参数之前访问网址,不成功,会出行如下信息。只有在指定时区之后访问才会成功。不成功的信息如下所示。

2.Before Route Predicate
Before Route Predicate 工厂携带一个时区参数 ZonedDateTime,用于匹配请求参数的时区参数,若在此时区之前访问服务网址可以访问成功,若是在此时区之后访问就会无法访问。

具体操作如下所示。
首先修改配置文件 application.yml ,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]- Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
如时区时间超过了上述配置文件的时间就会无法访问网址,网页就会报如下错误信息。

3.Between Route Predicate
Between Route Predicate 工厂携带两个参数,分别是时区参数 ZonedDateTime 的 datetime1 和 datetime2,而且时区时间 datetime2 要比时区时间 datetime1 要大,且请求只有在这个时间之内才会发生,即生效,否则访问服务网址都会显示找不到网页。注意:这两个参数,需要用逗号隔开。

具体操作如下所示。
修改配置文件 application.yml ,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]- Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
若访问网址的时间不在该时区之间,就会找不到网页,显示如下图所示的信息。

4.Cookie Route Predicate
Cookie Route Predicate 工厂携带了两个参数,分别是 name 和 regexp(正则表达式),第一个参数是 name,即 Cookie 路由匹配工厂需要携带的参数变量名,第二个参数是正则表达式。最后访问服务网址时只要变量名与其参数能够匹配正则表达式的规则,即视为有效参数,就能够匹配路由正常访问网址,否则不能成功访问网址。注意:这里的第二个参数也可以是一个固定的参数,如以下配置文件的regexp就为zzyy。

具体操作如下所示。
修改配置文件 application.yml,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]- Cookie=username,zzyy- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
测试网址主要由以下3中方法,分别是原生命令、postman、浏览器测试。测试过程如下所示。
方法1:原生命令测试
通过 win+R 打开运行框,然后输入 cmd 命令打开终端进行原生命令测试。首先测试不带 Cookie 谓词的,无法访问网址;然后测试带 Cookie 谓词的,访问网址成功。


方法2:postman测试,具体操作如下图所示。

方法3:浏览器测试

5.Header Route Predicate
Header Route Predicate 工厂携带两个参数,一个是 header 请求参数属性名,另一个是 regexp 正则表达式值,若请求头的属性能够匹配上请求属性名和对应的正则表达式值,则匹配路由,能够成功访问网址,否则不能成功访问网址。

具体操作如下所示。
修改配置文件 application.yml,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy- Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
配置信息配置成功后重新运行服务9527进行测试。主要通过原生命令测试和 postman 测试。
方法1:原生命令测试,主要测试携带参数和不携带参数,携带参数的又分为正则表达式值正确匹配的和不能正确匹配的。具体测试操作如下所示。

方法2:postman测试,在 Gateway 组件测试目录下新建测试文件 Gateway-Predicate-Header。具体操作如下所示。

6.Host Route Predicate
Host Route Predicate 工厂主要携带一组参数,用于匹配域名列表,采用 ant 样式的分隔模板,用.作为分隔符。以参数的主机地址作为匹配规则。其中**表示匹配任何内容。

具体操作步骤如下所示。
修改配置文件 application.yml,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy
# - Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式- Host=**.atguigu.com- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
配置信息修改成功后重新启动服务9527,然后进行测试。主要测试方法又原生命令测试和 postman 测试。
方法1:原生命令测试,主要测试携带参数和不携带参数的情况。

方法2:postman 测试

7.Path Route Predicate
Path Route Predicate 工厂携带了两个参数,分别是 PathMatcher patterns 和 matchTrailingSlash。匹配网址的访问路径。若匹配到路径则匹配路由。

具体操作步骤如下所示。
只需要在配置文件 application.yml 中修改配置信息 Path 即可,匹配上则进行路由匹配。就可以正常访问网址了。具体的配置信息如下图所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy
# - Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
# - Host=**.atguigu.com- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
因为上述测试每一次都使用了 Path 路由匹配,所以此处不在测试。
8.Query Route Predicate
Query Route Predicate 工厂主要携带了两个路径参数,直接将参数携带到网址后面即可,一个是属性名,一个是属性值,其中属性值可以是正则表达式。

具体操作步骤如下所示。
修改配置文件 application.yml,其中配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy
# - Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
# - Host=**.atguigu.com- Query=username,\d+ # 要有参数名username并且值还要是整数才能路由- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
配置信息完成后重新启动9527服务测试网址,如能够在网址中匹配上属性名和属性值,则能够成功访问网址。否则不能成功访问网址。

9.RemoteAddr Route Predicate
RemoteAddr Route Predicate 工厂携带了一组参数,是 IPv4 or IPv6 字符串,如192.168.0.1/16(CIDR无类别域间路由表示法)。该路由匹配工厂用于限制外部访问本机的 IP 。
IP 地址表示法——CIDR(Classless Iner-Domain Routing),无类别域间路由。


具体操作如下所示。
修改配置文件 application.yml,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy
# - Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
# - Host=**.atguigu.com
# - Query=username,\d+ # 要有参数名username并且值还要是整数才能路由- RemoteAddr=10.190.76.238/15 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
配置信息完成后重启9527服务,然后测试网址。若外部访问本机 IP 时,外部 IP 在配置文件中的远超地址谓词区间内,则能成功访问网址,否则不能。
10.Method Route Predicate
Method Route Predicate 工厂携带了方法参数,可以是一个也可以是多个。该路由工厂用来匹配 HTTP 方法,若能够匹配则能正确访问网址,否则不能。

具体操作如下所示。
修改配置文件 application.yml,具体配置信息如下所示。
server:port: 9527spring:application:name: cloud-gatewaycloud:consul:host: localhostport: 8500discovery:prefer-ip-address: trueservice-name: ${spring.application.name}gateway:routes:- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2025-10-04T22:51:11.375135900+08:00[Asia/Shanghai]
# - Before=2025-10-05T02:04:43.183785400+08:00[Asia/Shanghai]
# - Between=2025-10-05T02:18:05.733025800+08:00[Asia/Shanghai],2025-10-05T02:19:05.733025800+08:00[Asia/Shanghai]
# - Cookie=username,zzyy
# - Header=X-Request-Id,\d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
# - Host=**.atguigu.com
# - Query=username,\d+ # 要有参数名username并且值还要是整数才能路由
# - RemoteAddr=10.190.76.238/15 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。- Method=GET,POST- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
配置信息完成后重启9527服务,测试网址,根据上述配置信息,可以知道当 HTTP 请求为 GET 或 POST 时,能成功访问网址。
1.6.2.5.2 自定义路由匹配规则
当路由断言配置不能满足业务需求时,可以通过自定义断言来添加路由断言工厂,命名方式为×××RoutePredicateFactory,自定义断言配置的具体规则如下所示。
- 自定义路由断言工厂前缀可随意命名,后缀必须以 RoutePredicateFactory 结尾。
- 自定义路由断言工厂必须继承 AbstractRoutePredicateFactory 或实现 RoutePredicateFactory 接口。
路由断言工厂的架构概述图,如下所示。

然后再次查看 AfterRoutePredicateFactory 的源码,然后学习如何自定义路由断言工厂。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.cloud.gateway.handler.predicate;import org.springframework.cloud.gateway.support.AbstractConfigurable;public abstract class AbstractRoutePredicateFactory<C> extends AbstractConfigurable<C> implements RoutePredicateFactory<C> {public AbstractRoutePredicateFactory(Class<C> configClass) {super(configClass);}
}

1.自定义路由断言规则的步骤
- 首先,创建一个类 MyRoutePredicateFactory,需要继承 AbstractRoutePredicateFactory 抽象类。
- 然后,重写 apply 方法。
- 创建 apply 方法需要的静态内部类 MyRoutePredicateFactory.config,这个 Config 类就是路由断言规则,非常重要。
- 创建一个空参构造器,用于调用 super。
- 需要重写 shortcutFiledOrder 方法,否则在配置文件配置信息时无法使用短惆的格式(shortcut),只能使用 Fully Expanded Arguments 格式。
具体代码如下所示。
package com.atguigu.mygateway;import jakarta.validation.constraints.NotNull;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {public MyRoutePredicateFactory() {super(MyRoutePredicateFactory.Config.class);}@Overridepublic List<String> shortcutFieldOrder() {return Collections.singletonList("userType");}//@Overridepublic Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {return new GatewayPredicate() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");if (userType == null) return false;//如果说参数存在,就和config的数据进行比较if(userType.equals(config.getUserType())) {return true;}return false;}};}public static class Config{@NotNullprivate String userType;public String getUserType() {return userType;}public void setUserType(String userType) {this.userType = userType;}}
}
测试网址http://localhost:9527/pay/gateway/get/1?userType=diamond,显示如下图的信息,表示访问网址成功。

上一篇 7-SpringCloud-服务网关 Gateway-高级特性 Route
