Spring Cloud Gateway系列:路由断言工厂

发布时间 2023-10-31 00:39:39作者: Code技术分享

Spring Cloud Gateway 将路由匹配作为最基本的功能。而这个功能是通过路由断言工厂完成的。Spring Cloud Gateway 中包含了很多种内置的路由断言工厂。所有这些断言都可以匹配 HTTP 请求的不同属性,并且可以根据逻辑与状态,将多个路由断言工厂复合使用。

1、After路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-after-route-predicate-factory

该断言工厂的参数是一个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与该参数时间相比,若请求时间在参数时间之后,则匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://www.baidu.com
          predicates:
            - After=2027-01-20T17:42:47.789-07:00

API式

    @Bean
    public RouteLocator afterRouteLocator(RouteLocatorBuilder builder) {
        //当前时间加3天,将系统的默认时区设置为当前时区
        ZonedDateTime dateTime = LocalDateTime.now().plusDays(3).atZone(ZoneId.systemDefault());

        return builder.routes().route("after_route", ps ->
                        ps.after(dateTime).uri("https://www.baidu.com"))
                .build();
    }

2、Before路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-before-route-predicate-factory

该断言工厂的参数是一个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与该参数时间相比,若请求时间在参数时间之前,则匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://www.baidu.com
          predicates:
            - After=2027-01-20T17:42:47.789-07:00

API式

    @Bean
    public RouteLocator beforeRouteLocator(RouteLocatorBuilder builder) {
        //当前时间加3天,将系统的默认时区设置为当前时区
        ZonedDateTime dateTime = LocalDateTime.now().plusDays(3).atZone(ZoneId.systemDefault());

        return builder.routes().route("before_route", ps ->
                        ps.after(dateTime).uri("https://www.baidu.com"))
                .build();
    }

3、Between路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-between-route-predicate-factory

该断言工厂的参数是两个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与这两个参数时间相比,若请求时间在这两个参数时间之间,则匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: https://www.baidu.com
          predicates:
            - Between=2021-01-20T17:42:47.789-07:00,2027-01-20T17:42:47.789-07:00

API式

    @Bean
    public RouteLocator betweenRouteLocator(RouteLocatorBuilder builder) {
        ZonedDateTime endTime = LocalDateTime.now().plusDays(3).atZone(ZoneId.systemDefault());
        ZonedDateTime startTime = LocalDateTime.now().minusDays(3).atZone(ZoneId.systemDefault());
        return builder.routes().route("between_route",
                        ps -> ps.between(startTime, endTime)
                                .uri("https://www.baidu.com"))
                .build();
    }

4、Cookie路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-cookie-route-predicate-factory

该断言工厂中包含两个参数,分别是 cookie 的 key 与 value。当请求中携带了指定 key 与 value的 cookie 时,匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: https://www.baidu.com
          predicates:
            - Cookie=name,robin

API式

    @Bean
    public RouteLocator cookieRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("cookie_route",
                        ps -> ps.cookie("name", "robin").uri("https://baidu.com"))
                .build();
    }

image

5、Header路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-header-route-predicate-factory

该断言工厂中包含两个参数,分别是请求头 header 的 key 与 value。当请求中携带了指定 key与 value 的 header 时,匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: https://www.baidu.com
          predicates:
            - Header=X-Request-Id,\d+

API式

    @Bean
    public RouteLocator headerRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("header_route", ps ->
                        ps.header("X-Request-Id", "\\d+")
                                .uri("https://baidu.com"))
                .build();
    }

image

6、Host路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-host-route-predicate-factory

该断言工厂中包含的参数是请求头中的 Host 属性。当请求中携带了指定的 Host 属性值时,匹配成功,断言为 true。

例如

修改\etc中的hosts文件,为127.0.0.1 这个 ip指定多个主机名。

在该文件中添加如下内容:

image

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: host_route
          uri: https://www.baidu.com
          predicates:
            - Host=myhost:8000,scootersoftware.com=8000

API式

    @Bean
    public RouteLocator hostRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("host_route", ps ->
                        ps.host("myhost.com:8081","scootersoftware.com:8081")
                                .uri("https://baidu.com"))
                .build();
    }

image

7、Method路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-method-route-predicate-factory

该断言工厂用于判断请求是否使用了指定的请求方法,是 POST,还是 GET 等。当请求中使用了指定的请求方法时,匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: method_route
          uri: https://www.baidu.com
          predicates:
            - Method=GET,POST

API式

    @Bean
    public RouteLocator hostRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("method_route", ps ->
                        ps.method("GET","POST")
                                .uri("https://baidu.com"))
                .build();
    }

image

8、Path路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-path-route-predicate-factory

该断言工厂用于判断请求路径中是否包含指定的 uri。若包含,则匹配成功,断言为 true,此时会将该匹配上的 uri 拼接到要转向的目标 uri 的后面,形成一个统一的 uri

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: order_route
          uri: https://www.baidu.com  
          predicates:
            - Path=/order/**
        - id: product_route
          uri: https://www.jd.com
          predicates:
            - Path=/product/**

API式

    @Bean
    public RouteLocator pathRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("order_route", ps ->
                        ps.path("/order/**")
                                .uri("https://baidu.com"))
                .route("product_route", ps ->
                        ps.path("/product/**")
                                .uri("https://jd.com"))
                .build();
    }

image

image

9、Query路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-query-route-predicate-factory

以上两个 Query 断言的关系是“与”,即只有请求中同时包含了 username 与 password 参数,且username 参数值必须是以 ro 开头的,并且password等于123456就可以访问到。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: query_route
          uri: https://www.baidu.com
          predicates:
            - Query=username,ro.+
            - Query=password,123456

API式

    @Bean
    public RouteLocator queryRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("query_route", ps ->
                        ps.query("username", "ro.+")
                                .and()
                                .query("password", "123456")
                                .uri("https://baidu.com"))
                .build();
    }

image

10、RemoteAddr路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-remoteaddr-route-predicate-factory

该断言工厂用于判断提交请求的客户端 IP 地址是否在断言中指定的 IP 范围或 IP 列表中。若在,匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: addr_route
          uri: https://www.baidu.com
          predicates:
            - RemoteAddr=192.168.0.1/24

API式

    @Bean
    public RouteLocator remoteAddrRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("addr_route", ps ->
                        ps.remoteAddr("192.168.0.1/24")
                                .uri("https://baidu.com"))
                .build();
    }

由于当前浏览器提交请求的主机 IP 是 192.168.0.110,属于 192.168.0 网段,所以是可以访问到。

11、Weight路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-weight-route-predicate-factory

该断言工厂用于实现对同一组中的不同 uri 实现指定权重的负载均衡。路由中包含两个参数,分别是用于表示组 group,与权重 weight。对于同一组中的多个 uri 地址,路由器会根据设置的权重,按比例将请求转发给相应的 uri。

下面的路由用于指定对两个电商平台 taobao.com 与 jd.com 的访问权重为 8:2。以下两个路由都属于web-group组。

在大量请求的前提下,对于相同的请求,转发到 tabao 的机率占 8 成,转发到 jd 的机率占 2 成。但这不是一个绝对准确的访问比例,大体差不多。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: taobao_route
          uri: https://www.taobao.com
          predicates:
            - Weight=web-group,8
        - id: jd_route
          uri: https://www.jd.com
          predicates:
            - Weight=web-group,2

API式

    @Bean
    public RouteLocator weightRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("taobao_route", ps ->
                        ps.weight("web-group", 8)
                                .uri("https://taobao.com"))
                .route("jd_route", ps ->
                        ps.weight("web-group", 2)
                                .uri("https://jd.com"))
                .build();
    }

image

12、XForwardedRemoteAddr路由断言工厂

文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-xforwarded-remote-addr-route-predicate-factory

只要当前请求头中追加入 X-Forwarded-For 的 IP 出现在路由指定的 IP 列表中,则匹配成功,断言为 true。

配置式

server:
  port: 8080
spring:
  cloud:
    gateway:
      routes:
        - id: addr_route
          uri: https://www.baidu.com
          predicates:
            - XForwardedRemoteAddr=192.168.0.1/24

API式

    @Bean
    public RouteLocator xForwardedRemoteAddrRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes().route("addr_route", ps ->
                        ps.xForwardedRemoteAddr("192.168.0.1/24")
                                .uri("https://baidu.com"))
                .build();
    }

13、优先级

当一个应用中对于相同的路由断言工厂,分别通过配置式与 API 式都进行了设置,且设置的值不同,此时它们的关系有两种:

  1. 或的关系:例如,Header 路由断言工厂、Query 路由断言工厂

  2. 优先级关系:例如,After、Before、Between 路由断言工厂。此时配置式的优先级要高于 API 式的。