Path 路由断言工厂需要两个参数:一个 Spring PathMatcher 模式列表和一个名为 matchTrailingSlash 的可选标记(默认为 true)。语法如下:
Pth=uri,matchTrailingSlash
注意,matchTrailingSlash 标记用于指示网关是否应该匹配结尾斜杠。当设置为 true 时,网关将匹配包含结尾斜杠和不包含结尾斜杠的 URL,并将它们视为相同的资源。当设置为 false 时,网关将只匹配精确的 URL 路径,不考虑结尾斜杠。这个标记通常用于配置网关的路由规则,以确定是否应该考虑结尾斜杠的影响。
例如:
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 工厂使用。
为了更方便地访问这些变量,我们提供了一个实用方法(名为 get)。下面的示例展示了如何使用 get 方法:
Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");将“Gateway 搭建网关服务”项目的配置文件 bootstrap.yml 内容使用如下配置替换:
server: # 网关端口 port: 9000 spring: application: # 服务名称 name: gateway-demo01 cloud: # nacos nacos: discovery: server-addr: localhost:8848 username: nacos password: nacos group: DEFAULT_GROUP # 网关路由配置 gateway: # 网关路由配置 routes: # 路由id,自定义,只要唯一集合 - id: service-order # 路由的目标地址,lb 就是负载均衡,后面跟服务名 # 需要集成 nacos 等服务注册中心 uri: lb://service-order # 路由断言,也就是判断请求是否符合路由规则的条件 predicates: # 按照路径匹配,只要以 /order/ 开头就符合要求 - Path=/order/**
修改好配置后,重启网关服务,使用 Postman 访问订单服务。如下图:

从上图可知,访问接口成功。如果将 Path 断言的地址修改为“/order2/**”,然后重启网关服务,继续访问接口,如下图:

地址不匹配,导致访问失败。
下面是 Path 路由断言工厂源码分析:
package org.springframework.cloud.gateway.handler.predicate;
//...
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
private static final Log log = LogFactory.getLog(RoutePredicateFactory.class);
private static final String MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY = "matchOptionalTrailingSeparator";
private PathPatternParser pathPatternParser = new PathPatternParser();
public PathRoutePredicateFactory() {
super(Config.class);
}
public void setPathPatternParser(PathPatternParser pathPatternParser) {
this.pathPatternParser = pathPatternParser;
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("patterns", MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY);
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.GATHER_LIST_TAIL_FLAG;
}
// 主要代码逻辑
@Override
public Predicate<ServerWebExchange> apply(Config config) {
final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
synchronized (this.pathPatternParser) {
pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator());
config.getPatterns().forEach(pattern -> {
PathPattern pathPattern = this.pathPatternParser.parse(pattern);
pathPatterns.add(pathPattern);
});
}
return exchange -> {
// 获取路径信息
PathContainer path = parsePath(exchange.getRequest().getURI().getPath());
// 使用配置的模式匹配路径
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path))
.findFirst();
if (optionalPathPattern.isPresent()) {
// 路径匹配成功
PathPattern pathPattern = optionalPathPattern.get();
traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
} else {
traceMatch("Pattern", config.getPatterns(), path, false);
return false;
}
};
}
private static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
if (log.isTraceEnabled()) {
String message = String.format("%s \"%s\" %s against value \"%s\"",
prefix, desired, match ? "matches" : "does not match", actual);
log.trace(message);
}
}
@Validated
public static class Config {
private List<String> patterns = new ArrayList<>();
private boolean matchOptionalTrailingSeparator = true;
// 配置信息
}
}