一、 断言(Predicate)是什么?
在 Spring Cloud Gateway 中,断言是路由规则的一部分。它是一个 Java 8 的 Predicate,接收一个 ServerWebExchange对象作为输入,返回一个布尔值。如果返回 true,则表示该请求匹配当前路由,网关会将请求转发到指定的 URI;否则,继续匹配下一个路由。
二、 断言的两种写法:短写法 vs 长写法
在配置文件(如 application.yml)中,断言有两种配置方式,它们的功能完全一致,只是写法不同。
1. 短写法(Shortcut Configuration)
这是最常用的写法,简洁明了。格式为:断言名称=参数1, 参数2, ...。
-
示例分析:
predicates: - Path=/api/order/** - Path=/api/product/**-
这里的
Path是断言名称。 -
/api/order/**是参数,表示匹配以/api/order/开头的所有路径。
-
2. 长写法(Full Configuration)
这种写法更显式,结构更清晰,适合复杂的断言配置。格式为:name: 断言名称, args: {参数名: 参数值}。
-
示例分析:
predicates: - name: Path args: patterns: /api/order/** matchTrailingSlash: true-
name指定了断言类型。 -
args是一个 Map,包含了该断言所需的所有参数。例如Path断言除了路径模式patterns,还可以配置matchTrailingSlash(是否匹配尾部斜杠)。
-
三、 Query 断言详解:如何根据查询参数路由?
Query断言用于匹配 HTTP 请求中的查询参数(Query Parameters)。这是实现灰度发布、A/B 测试或特定功能开关的利器。
1. 基础用法:匹配特定参数值
假设我们有一个搜索接口,只有当用户携带了特定的搜索关键词时,才允许访问。
-
配置示例:
predicates: - name: Query args: param: q regexp: haha-
含义: 匹配请求中包含
q参数,且其值必须符合正则表达式haha的请求。 -
场景: 例如
https://example.com/search?q=haha会被匹配,而https://example.com/search?q=hello则不会。
-
2. 进阶用法:仅检查参数是否存在
有时候我们只需要检查某个参数是否存在,而不关心它的具体值。
-
配置示例:
predicates: - Query=q- 含义: 只要请求 URL 中包含
q参数(无论值是多少),都会匹配。例如?q=123或?q=都会匹配。
- 含义: 只要请求 URL 中包含
3. 组合使用:Path + Query
通常我们会将 Path和 Query组合使用,实现更精确的路由控制。
-
配置示例:
predicates: - Path=/api/search - Query=q, haha- 含义: 只有当请求路径是
/api/search且 查询参数q的值为haha时,才会触发该路由。
- 含义: 只有当请求路径是
四、 自定义断言工厂:VipRoutePredicateFactory
虽然 Gateway 内置了丰富的断言(如 Path, Query, Header, Cookie 等),但业务需求千变万化。当内置断言无法满足需求时,我们需要自定义断言。
1. 需求场景
假设我们需要实现一个"VIP 通道",只有当请求中携带了特定的 vipCode参数且值为 123456时,才允许访问某个高优先级的服务。
2. 代码实现
我们需要继承 AbstractRoutePredicateFactory并实现 apply方法。
java
package com.qcby.gateway.predicate;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {
// 1. 构造函数:指定配置类
public VipRoutePredicateFactory() {
super(Config.class);
}
// 2. 定义短写法的参数顺序
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "value");
}
// 3. 核心逻辑:定义匹配规则
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
ServerHttpRequest request = serverWebExchange.getRequest();
// 获取请求中的查询参数
String paramValue = request.getQueryParams().getFirst(config.getParam());
// 判断参数是否存在且值是否匹配
if (StringUtils.hasText(paramValue) && paramValue.equals(config.getValue())) {
return true;
}
return false;
}
};
}
// 4. 配置类:定义断言所需的参数
@Validated
public static class Config {
@NotEmpty
private String param; // 参数名,如 vipCode
@NotEmpty
private String value; // 参数值,如 123456
// Getter & Setter
public String getParam() { return param; }
public void setParam(String param) { this.param = param; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
}
自定义断言工厂名字一定要在去掉RoutePredicateFactory之后和配置文件中的名字匹配
3. 配置使用
编写完代码后,重启网关服务,即可在配置文件中使用这个自定义断言。
spring:
cloud:
gateway:
routes:
- id: vip-route
uri: lb://vip-service
predicates:
- Vip=vipCode, 123456
- 含义: 只有当请求中包含
vipCode=123456时,请求才会被转发到vip-service。
五、 总结
Spring Cloud Gateway 的断言机制非常灵活且强大。通过掌握 Path、Query等内置断言的长短写法,我们可以轻松实现复杂的路由逻辑。而通过继承 AbstractRoutePredicateFactory编写自定义断言,我们可以将业务逻辑下沉到网关层,实现更精细化的流量控制。