断言就是一套规则,用来判断"什么样的请求,该走哪条路"。
网关内部配置了多个路由规则,每个规则都有对应的断言(Predicate)。网关会拿这个请求去逐一匹配这些断言条件
1.写法
短写法:
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order #lb表示负载均衡
# 断言
predicates: #短写法
- Path=/api/order/** #所有以api/order开头的路劲转给service-order处理
order: 0 #优先级,值越小优先级越高
全写法:
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order #lb表示负载均衡
# 断言
predicates:
- name: Path #全写法
args:
pattern: /api/order/**
match-trailing-slash: true
2.规则

3.测试
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order #lb表示负载均衡
# 断言
predicates:
- name: Path
args:
pattern: /api/order/**
match-trailing-slash: true
order: 0 #优先级,值越小优先级越高
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/** #所有以api/product开头的路径转给service-product处理
# <--------全写法演示-------->
- id: bing-route
uri: https://cn.bing.com
predicates:
- name: Path
args:
pattern: /search
- name: Query
args:
param: q
regexp: hello world
# <---------全写法演示-------->
# <---------短写法演示------->
- id: deepseek-route
uri: https://www.deepseek.com
predicates:
- Path=/search
- Query=q,hello
# <---------短写法演示--------->
这里再写两个断言,一个用全写法,一个用短写法,体会两种写法的优劣
启动GatewayMainApplication,等待控制台出现Started GatewayMainApplication,即说明服务启动完毕,再执行下步操作 。

打开浏览器或者apipsot/postman/apifox等接口测试工具 (这里使用apipost)
浏览器网址口或者测试工具请求口输入localhost/search/?q=hello world 回车


可以看到,测试通过,匹配成功。
继续测试deepseek-route


如图可以看到,浏览器和apipost测试断言匹配,测试通过
4.自定义断言工厂
创建断言工厂
在com.atguigu.gateway包下连包带类创建VipRoutePredicateFactory类

创建完成后,我们使用ctrl+n来搜索断言工厂源码QueryRoutePredicateFactory类
我们可以通过源码看到,自定义一个断言工厂需要:
1.继承抽象父类 AbstractRoutePredicateFactory并需要指定一个配置类作为泛型参数
2.定义配置类,并添加getter和setter方法
3.实现构造函数调用 super(Config.class)
4.实现shortcutFieldOrder(可选):定义快捷配置的参数顺序
5.实现apply方法:编写核心逻辑,利用 ServerWebExchange
和 Config
进行判断,返回 Predicat
6.注册为Bean:通常使用 @Component
注解将其声明为一个Spring Bean,这样框架就能自动发现它。
package org.springframework.cloud.gateway.handler.predicate;
import jakarta.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory<Config> {
//定义常量字符串 k-v
public static final String PARAM_KEY = "param";
public static final String REGEXP_KEY = "regexp";
//构造函数
public QueryRoutePredicateFactory() {
super(Config.class);
}
//短写法
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "regexp");
}
//这是核心方法,它创建了实际的断言逻辑
public Predicate<ServerWebExchange> apply(final Config config) {
return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
if (!StringUtils.hasText(config.regexp)) {
return exchange.getRequest().getQueryParams().containsKey(config.param);
} else {
List<String> values = (List)exchange.getRequest().getQueryParams().get(config.param);
if (values == null) {
return false;
} else {
Iterator var3 = values.iterator();
String value;
do {
if (!var3.hasNext()) {
return false;
}
value = (String)var3.next();
} while(value == null || !value.matches(config.regexp));
return true;
}
}
}
//返回当前断言所使用的配置对象
public Object getConfig() {
return config;
}
public String toString() {
return String.format("Query: param=%s regexp=%s", config.getParam(), config.getRegexp());
}
};
}
//内部配置类
@Validated
public static class Config {
private @NotEmpty String param;
private String regexp;
public Config() {
}
public String getParam() {
return this.param;
}
public Config setParam(String param) {
this.param = param;
return this;
}
public String getRegexp() {
return this.regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
}
}
下面,我们根据源码来照猫画虎,定义一个自己的断言工厂VipRoutePredicateFactory
package com.atguigu.gateway.predicate;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
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;
//注册为bean,交给spring管理
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {
//构造函数调用
public VipRoutePredicateFactory() {
super(Config.class);
}
@Override
// 实现apply方法
public Predicate<ServerWebExchange> apply(Config config) {
System.out.println("进入自定义断言工厂");
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
// ServerWebExchange内部封装了请求参数和响应结果
ServerHttpRequest request = serverWebExchange.getRequest();
// 获取参数
String first = request.getQueryParams().getFirst(config.param);
//判空并判等
if (StringUtils.hasText(first) && first.equals(config.value)) {
return true;
}
return false;
}
};
}
// 定义参数顺序,重写shortcutFieldOrder方法
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "value");
}
/**
* 定义配置类
*/
@Validated
public static class Config {
@NotEmpty
private String param;
@NotEmpty
private String value;
public @NotEmpty String getParam() {
return param;
}
public @NotEmpty String getValue() {
return value;
}
public void setParam(@NotEmpty String param) {
this.param = param;
}
public void setValue(@NotEmpty String value) {
this.value = value;
}
}
}
接着我们需要在配置文件中配置自定义断言工厂

5.测试(自定义断言工厂)
我们在启动GatewayMainApplication,然后等待服务启动完毕,然后浏览器访问

首先测试短写法演示
可以看到localhost/search?q=hello&user=yuexihuachen 访问deepseek成功
然后测试全写法演示 此时q=hello world,localhost/search?q=hello world&user=yuexihuachen访问bing也成功

最后我们再改变user的值,看看是否会失败,localhost/search?q=hello world&user=yuexihuache

果不其然,访问未通过,因为user参数的值与我们在idea中配置文件中不一致,返回404错误码。