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

相关推荐
a1117761 小时前
个人展示页面(html 线条交互)
前端·开源·html
A懿轩A1 小时前
【Maven 构建工具】Maven + JUnit5 单元测试实战:测试级别、注解、断言与 Maven test 阶段
java·单元测试·maven
RichardLau_Cx1 小时前
零依赖!纯前端 AI 辅助病例管理系统 aiCaseManage:无后端也能实现诊疗行为核验
前端·人工智能·前端开发·localstorage·医疗科技·ai辅助开发·零依赖项目
Coder_Boy_2 小时前
以厨房连锁故事为引,梳理Java后端全技术脉络(JVM到云原生,总结篇)
java·jvm·spring boot·分布式·spring·云原生
悠闲蜗牛�2 小时前
Kubernetes从零到集群:本地Minikube环境搭建与Spring Cloud微服务运维实战
spring cloud·微服务·kubernetes
特立独行的猫a2 小时前
基于HarmonyOS ArkTS的MVVM架构最佳实践
华为·架构·harmonyos·mvvm·最佳实战
Zhu_S W2 小时前
Docker 完全指南:Java 开发者的容器化实践
java·docker·容器
Zhu_S W2 小时前
EasyExcel动态表头详解
java·linux·windows
敲代码的哈吉蜂2 小时前
Nginx配置文件的管理及优化参数
java·服务器·nginx