Spring Cloud Gateway中uri和path一样时路径被删除,导致返回404

现象

路由配置

- id: r_invitation
  uri: lb://Gateway #eureka中服务名为Gateway
  predicates:
    - Path=/gateway/**
  • 请求路径:http://ip:port/gateway/hi
  • 期望跳转到:lb://Gateway/gateway/hi
  • 实际跳转到:lb://Gateway/hi

导致无法请求到路径,返回404。

原因分析

Spring5.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);
		}

	}
}
  •  在RewritePathGatewayFilterFactory工厂中生成的过滤器,会将路径替换。
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需要需要进一步分析(偷懒先去忙别的了)。

你可能感兴趣的:(网关,SpringCloud,gateway404,path404)