Gateway-断言

断言就是一套规则,用来判断"什么样的请求,该走哪条路"。

网关内部配置了多个路由规则,每个规则都有对应的断言(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方法:编写核心逻辑,利用 ServerWebExchangeConfig 进行判断,返回 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错误码。

相关推荐
西贝爱学习2 小时前
【JDK 11 安装包免费下载 免登录Oracle 】jdk11与jdk8有什么区别?
java·开发语言
Vallelonga2 小时前
Rust 中的 static 和 const
开发语言·经验分享·rust
s9123601012 小时前
【rust】 pub(crate) 的用法
开发语言·后端·rust
宠友信息2 小时前
类似小红书垂直社区APP小程序源码
java·开发语言·微信小程序·小程序·uni-app·开源·web app
10001hours3 小时前
C语言第21讲
c语言·开发语言
yujkss3 小时前
23种设计模式之【享元模式】-核心原理与 Java实践
java·设计模式·享元模式
_extraordinary_3 小时前
Java Servlet(一)--- Servlet hello world的写法,smart tomcat,Servlet代码中的常见问题
java·servlet·tomcat
智界软体库3 小时前
《IDEA 2025长效使用配置指南:有效期配置至2099年实战之JetBrains全家桶有效》
java·ide·intellij-idea
程序员大辉3 小时前
python代码案例分享,python实现代码雨,动画显示,pygame使用教程
开发语言·python