Spring Cloud Gateway 核心实战:断言(Predicate)的长短写法与自定义工厂详解

一、 断言(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=都会匹配。

3. 组合使用:Path + Query

通常我们会将 PathQuery组合使用,实现更精确的路由控制。

  • 配置示例:

    复制代码
    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 的断言机制非常灵活且强大。通过掌握 PathQuery等内置断言的长短写法,我们可以轻松实现复杂的路由逻辑。而通过继承 AbstractRoutePredicateFactory编写自定义断言,我们可以将业务逻辑下沉到网关层,实现更精细化的流量控制。

相关推荐
五点六六六4 小时前
基于 AST 与 Proxy沙箱 的局部代码热验证
前端·设计模式·架构
冉冰学姐4 小时前
基于ssm的技能比赛报名管理系统29817vn0(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
java·数据库·spring·ssm 框架应用
发现一只大呆瓜6 小时前
SSO单点登录:从同域到跨域实战
前端·javascript·面试
发现一只大呆瓜6 小时前
告别登录中断:前端双 Token无感刷新
前端·javascript·面试
代码雕刻家7 小时前
3.5.Maven-依赖管理-依赖配置&依赖传递
java·maven
Cg136269159747 小时前
JS-对象-Dom案例
开发语言·前端·javascript
!chen7 小时前
MyBatis-plus拓展之字段类型处理器、自动填充和乐观锁
java·tomcat·mybatis
无限大67 小时前
《AI观,观AI》:善用AI赋能|让AI成为你深耕核心、推进重心的“最强助手”
前端·后端
Jin、yz7 小时前
JAVA 八股
java·开发语言
烛阴8 小时前
Claude Code Skill 从入门到自定义完整教程(Windows 版)
前端·ai编程·claude