Spring Cloud Gateway -- 关于Path的配置

直接用官方文档的例子好了。。假设网关的地址是localhost:8080

在符合时间范围内才匹配

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://localhost:8001
        predicates:
      # 在某个时间之前的请求才会被转发到 http://localhost:8001,
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
      # 在某个时间之后的请求才会被转发
      # - After=2017-01-20T17:42:47.789-07:00[America/Denver]
      # 在某个时间段之间的才会被转发
      # - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

按表单名进行匹配

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://example.org
        predicates:
        # 名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发
        - Cookie=chocolate, ch.p

按请求头匹配

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://example.org
        predicates:
        # 携带参数X-Request-Id或者满足\d+的请求头才会匹配
        - Header=X-Request-Id, \d+

按Host主机名匹配

  • 访问特定的主机名才能匹配,若网关的地址是localhost:8080, 主机名是qiying.com:8080(80端口被占了),那么只有访问http:qiying.com:8080/app/test 请求才会转发到localhost:8001/app/test。
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Host=qiying.com:8080

若直接访问localhost:8080/test 则网关会匹配失败


image.png

按请求方法匹配

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://example.org
        predicates:
        # 只有GET方法才会匹配
        - Method=GET

按请求路径匹配

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/app/{path}
      # - Path=/app/test

结合一些filter更好用哦

PrefixPath Filter 在请求路径前加上自定义的路径

假如应用访问地址是localhost:8001/app, 接口地址是/test,这里设置了prefixPath为/app, 那么当你访问localhost:8080/test, 网关在帮你转发请求之前,会在/test 前加上/app,转发时的请求就变成了localhost:8001/app/test。

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/{path}
        filters:
        - PrefixPath=/app

RewritePath Filter 重写请求路径

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/{path}
        filters:
        # 访问localhost:8080/test, 请求会转发到localhost:8001/app/test
        - RewritePath=/test, /app/test

这个filter比较灵活的就是可以进行正则匹配替换,如下的例子就是当请求localhost:8080/test时,匹配所有以/开头的路径,然后在前面加上/app,所以现在请求变成了localhost:8080/app/test。然后转发时的url变成了localhost:8001/app/test 。在测试的时候,这个filter是没办法使用模板进行匹配的。可能是因为它是用的正则进行匹配替换,所以没办法使用模板吧

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/test
        filters:
        - RewritePath=(?<oldPath>^/), /app$\{oldPath}

值得注意的是在yml文档中 $ 要写成 $\ 。替换路径是使用的是String.replaceAll()方法,这个方法和replace()不同,是根据正则进行替换的。具体的替换规则感兴趣的话可以去了解一下Pattern。
https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

public GatewayFilter apply(RewritePathGatewayFilterFactory.Config config) {
    // 可以看到在RewritePathGatewayFilterFactory中  $\ 会被替换回 $ 
    String replacement = config.replacement.replace("$\\", "$");
    return (exchange, chain) -> {
      ServerHttpRequest req = exchange.getRequest();
      ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
      String path = req.getURI().getRawPath();
      String newPath = path.replaceAll(config.regexp, replacement);
      ServerHttpRequest request = req.mutate().path(newPath).build();
      exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
      return chain.filter(exchange.mutate().request(request).build());
    };
  }

SetPath Filter 通过模板设置路径

这个filter的使用方式比较简单。就是匹配到满足/a开头的路径后重新设置路径为以/app开头。

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/a/{path}
        filters:
        - SetPath=/app/{path}

看到这里,大家应该大概了解了模板{...}的具体用法了吧。模板里面的变量可通过ServerWebExchange.getAttributes()获得。具体的请参看官方文档。

最后的最后,因为好奇三种关于path的filter能不能一起用,所以做了个测试。

  • 首先先看PrefixPath 和 RewritePath。
    通过debuge可以发现,先进入PrefixPathGatewayFilterFactory的apply()方法,然后执行后面的filter链。然后进入RewritePathGatewayFilterFactory的apply()方法,再执行后面的filter。然后执行PrefixPathGatewayFilterFactory#apply()的return里的方法,开始替换路径,最后再执行RewritePathGatewayFilterFactory#apply()的return方法。所以先进行的是前缀替换,在进行正则替换。配置如下,请求localhost:8080/test 最终 会变成localhost:8001/api/app/test
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/{path}
        filters:
        - PrefixPath=/app
        - RewritePath=(?<oldPath>^/), /api$\{oldPath}
  • 然后三种filter一起测试。。事实证明没办法正常工作。虽然最先进入的filter是SetPathGatewayFilterFactory。
    在理想情况,如果按照filter的执行顺序,如下配置下的url最终会变成localhost:8001/api/app/a/test。
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8001
        predicates:
        - Path=/{path}
        filters:
        - PrefixPath=/app
        - RewritePath=(?<oldPath>^/), /api$\{oldPath}
        - SetPath=/a/{path}

但是!!我们来看看SetPathGatewayFilterFactory的apply方法。一进入这个方法,{path}(/test)就被记录下来了。当另外两个filter的return方法执行完了之后,才会执行这个方法的return 方法。最后路径就会被替换为/a/test。所以SetPathFilter和另外两个Filter是没办法同时生效滴!

 public GatewayFilter apply(SetPathGatewayFilterFactory.Config config) {
    UriTemplate uriTemplate = new UriTemplate(config.template);
    return (exchange, chain) -> {
      PathMatchInfo variables = (PathMatchInfo)exchange.getAttribute(ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
      ServerHttpRequest req = exchange.getRequest();
      ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
      Map uriVariables;
      if (variables != null) {
        uriVariables = variables.getUriVariables();
      } else {
        uriVariables = Collections.emptyMap();
      }

      URI uri = uriTemplate.expand(uriVariables);
      String newPath = uri.getRawPath();
      exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
      ServerHttpRequest request = req.mutate().path(newPath).build();
      return chain.filter(exchange.mutate().request(request).build());
    };
  }

最后的最后的最后。。来打脸了。这个filter的执行顺序和配置定义的顺序是有关系的。。

 filters:
        - SetPath=/a/{path}
        - RewritePath=(?<oldPath>^/), /api$\{oldPath}
        - PrefixPath=/app

按照这个顺序的话,先执行的就是将 /test替换为 /a/test,然后替换为/api/a/test,最后替换为/app/api/test。。
由此得出的结论是只要SetPath这个filter在其他两个filter之前执行的话还是不冲突的。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281