Spring Cloud Gatewayでは、uriとpathが同じ時に経路が削除され、404に戻る.


現象
ルートの設定
- id: r_invitation
  uri: lb://Gateway #eureka     Gateway
  predicates:
    - Path=/gateway/**
  • は、パスを要求する.http://ip:port/gateway/hi
  • ジャンプを期待しています.lb://Gateway/hi
  • 実際にジャンプします.lb://Gateway/hi
  • パスを要求できなくなり、404を返します.
    原因分析
    Spring 5.0はreactive(反応式)のプログラミングコンセプトを使用し始め、SCGはデフォルトでは登録センターに登録されたサービスを主導してデフォルトのルートルールを作成し、優先とカスタムルールを切って、カスタムルールが無効になります.
    ソースは以下の通りです
    デフォルトではeureka上のすべてのサービスに対してpath=/service_を作成します.id/*のルートマッチング
    public class GatewayDiscoveryClientAutoConfiguration {
    
    public static List initPredicates() {
    		ArrayList 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;
    	}
    
    	public static List initFilters() {
    		ArrayList definitions = new ArrayList<>();
    
    		// add a filter that removes /serviceId by default
    		FilterDefinition filter = new FilterDefinition();
    		filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
    		String regex = "'/' + serviceId + '/(?.*)'";
    		String replacement = "'/${remaining}'";
    		filter.addArg(REGEXP_KEY, regex);
    		filter.addArg(REPLACEMENT_KEY, replacement);
    		definitions.add(filter);
    
    		return definitions;
    	}
    
    	@Bean
    	public DiscoveryLocatorProperties discoveryLocatorProperties() {
    		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
    		properties.setPredicates(initPredicates());
    		properties.setFilters(initFilters());
    		return properties;
    	}
    
    	/**
    	 * @deprecated In favor of the native reactive service discovery capability.
         *        , false   
    	 */
    	@Configuration(proxyBeanMethods = false)
    	@Deprecated
    	@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",
    			havingValue = "false")
    	public static class BlockingDiscoveryClientRouteDefinitionLocatorConfiguration {
    
    		@Bean
    		@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
    		public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
    				DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
    			return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
    		}
    
    	}
    }
  •  RewritePathGateway FilterFactory工場で生成されたフィルタは、経路を入れ替えます.
  • package org.springframework.cloud.gateway.filter.factory;
    ...
    public class RewritePathGatewayFilterFactory
    		extends AbstractGatewayFilterFactory {
    ...
    
    return new GatewayFilter() {
    			@Override
    			public Mono filter(ServerWebExchange exchange,
    					GatewayFilterChain chain) {
    				ServerHttpRequest req = exchange.getRequest();
    				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(GATEWAY_REQUEST_URL_ATTR, request.getURI());
    
    				return chain.filter(exchange.mutate().request(request).build());
    			}
    
    ...
    }
    解決策
    反応式をオフにしてルートを作成します.
    spring.cloud.discovery.reactive.enabled = false
    注:この機能は5.2バージョンで利用できます.github上の5.3バージョンの原発発見を確認したら、この構成はすでに削除されました.例えば5.3を使うにはさらに分析が必要です.