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编写自定义断言,我们可以将业务逻辑下沉到网关层,实现更精细化的流量控制。

相关推荐
.道阻且长.12 小时前
C++ string 操作指南:接口解析
java·c语言·开发语言·c++
anyup12 小时前
分享 5 套 uni-app 实用主题,一键适配暗黑模式
前端·uni-app·视觉设计
李白的天不白12 小时前
vue3 ts 配置smartadmin相关配置
前端
蚰蜒螟12 小时前
Java 对象的内存密语:从字段偏移量计算到 Unsafe 访问的完整链路
java·开发语言
IT 行者12 小时前
GitHub Spec Kit 实战(六):/speckit.implement 怎么用、怎么审、怎么发现 spec 阶段的遗漏——五部曲收官
java·驱动开发·github·ai编程·claude
带娃的IT创业者12 小时前
深度解析:从 GitHub 热门项目看 SEO 自动化的技术架构演进
架构·自动化·github·seo·技术架构·反爬虫
起这个名字12 小时前
Typescript 装饰器执行顺序
前端
星辰_mya12 小时前
CountDownLatch深度解析
java·开发语言·后端·架构
LDX前端校草12 小时前
position属性值及用法
前端·javascript·面试
伊甸312 小时前
从企业级项目学敏感词过滤:DFA算法与双层缓存实战
java·算法·缓存