从zuul迁移到spring cloud gateway时如何“删除全局前缀('/api')然后转到lb://”

Jas*_* Yu 1 java spring-cloud netflix-zuul spring-cloud-gateway

我想从 zuul 迁移到 spring 云网关,我不想更改我以前的应用程序的配置。我想知道如何处理带有“/api/ + 'serviceId'”的 url,路由到 lb://serviceId

之前的 zuul 配置

zuul: 
  prefix: /api
Run Code Online (Sandbox Code Playgroud)

eureka 有很多服务注册,我不想为每个服务注册一个路由。

例如。由 org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator 自动生成的路由

{
"route_id": "CompositeDiscoveryClient_APIGATEWAY",
"route_definition": {
  "id": "CompositeDiscoveryClient_APIGATEWAY",
  "predicates": [
    {
      "name": "Path",
      "args": {
        "pattern": "/apigateway/**"
      }
    }
  ],
  "filters": [
    {
      "name": "RewritePath",
      "args": {
        "regexp": "/apigateway/(?<remaining>.*)",
        "replacement": "/${remaining}"
      }
    }
  ],
  "uri": "lb://APIGATEWAY",
  "order": 0
}
Run Code Online (Sandbox Code Playgroud)

我想要的是

 {
"route_id": "CompositeDiscoveryClient_APIGATEWAY",
"route_definition": {
  "id": "CompositeDiscoveryClient_APIGATEWAY",
  "predicates": [
    {
      "name": "Path",
      "args": {
        "pattern": "/api/apigateway/**"
      }
    }
  ],
  "filters": [
    {
      "name": "RewritePath",
      "args": {
        "regexp": "/api/apigateway/(?<remaining>.*)",
        "replacement": "/${remaining}"
      }
    }
  ],
  "uri": "lb://APIGATEWAY",
  "order": 0
}
Run Code Online (Sandbox Code Playgroud)

我如何配置我的路线以获得我想要的

而且我还找到了源代码

    public static List<PredicateDefinition> initPredicates() {
    ArrayList<PredicateDefinition> definitions = new ArrayList<>();
    // TODO: add a predicate that matches the url at /serviceId?

    // add a predicate that matches the url at /serviceId/**
    PredicateDefinition predicate = new PredicateDefinition();
    predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
    predicate.addArg(PATTERN_KEY, "'/'+serviceId+'/**'");
    definitions.add(predicate);
    return definitions;
}
Run Code Online (Sandbox Code Playgroud)

“'/'+serviceId+'/**'” 没有前缀

[2019-01-10] 更新 我认为@spencergibb 的建议是一个很好的解决方案,但是我在 (SpEL) 上遇到了新的问题,我尝试了很多方法:

args:
  regexp: "'/api/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
  replacement: '/${remaining}'
args:
  regexp: "'/api/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
  replacement: "'/${remaining}'"
Run Code Online (Sandbox Code Playgroud)

启动失败

Origin: class path resource [application.properties]:23:70
Reason: Could not resolve placeholder 'remaining' in value "'${remaining}'"
Run Code Online (Sandbox Code Playgroud)

当我使用转义符“\”时

args:
  regexp: "'/api/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
  replacement: '/$\{remaining}'
Run Code Online (Sandbox Code Playgroud)

它开始成功,但我在运行时出现异常

org.springframework.expression.spel.SpelParseException: Expression [/$\{remaining}] @2: EL1065E: unexpected escape character.
at org.springframework.expression.spel.standard.Tokenizer.raiseParseException(Tokenizer.java:590) ~[spring-expression-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.expression.spel.standard.Tokenizer.process(Tokenizer.java:265) ~[spring-expression-5.0.5.RELEASE.jar:5.0.5.RELEASE]
Run Code Online (Sandbox Code Playgroud)



更新 2

我发现在org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory,有一个替代处理“\”

...    
@Override
        public GatewayFilter apply(Config config) {
            String replacement = config.replacement.replace("$\\", "$");
            return (exchange, chain) -> {
...
Run Code Online (Sandbox Code Playgroud)

当谈到SpelParseException没有

spe*_*ibb 5

您可以自定义通过属性使用的自动过滤器和谓词。

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          predicates:
            - name: Path
              args:
                pattern: "'/api/'+serviceId.toLowerCase()+'/**'"
          filters:
            - name: RewritePath
              args:
                regexp: "'/api/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
                replacement: "'/${remaining}'"
Run Code Online (Sandbox Code Playgroud)

请注意,值(即args.patternargs.regexp)都是 Spring 表达式语言 (SpEL) 表达式,因此单引号+等...

如果不同的路由需要有不同的前缀,则需要在属性中定义每个路由。