Skip to content

Commit 066b28c

Browse files
committed
Add syntactic sugar for URI paths (gh-3293)
Signed-off-by: Arnav <arnav.vyas06@gmail.com>
1 parent bad23ad commit 066b28c

3 files changed

Lines changed: 117 additions & 81 deletions

File tree

.settings.xml

Lines changed: 45 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,47 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<!--
3-
~ Copyright 2013-2017 the original author or authors.
4-
~
5-
~ Licensed under the Apache License, Version 2.0 (the "License");
6-
~ you may not use this file except in compliance with the License.
7-
~ You may obtain a copy of the License at
8-
~
9-
~ https://www.apache.org/licenses/LICENSE-2.0
10-
~
11-
~ Unless required by applicable law or agreed to in writing, software
12-
~ distributed under the License is distributed on an "AS IS" BASIS,
13-
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
~ See the License for the specific language governing permissions and
15-
~ limitations under the License.
16-
~
17-
-->
2+
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
5+
https://maven.apache.org/xsd/settings-1.0.0.xsd">
186

19-
<settings>
20-
<servers>
21-
<server>
22-
<id>repo.spring.io</id>
23-
<username>${env.CI_DEPLOY_USERNAME}</username>
24-
<password>${env.CI_DEPLOY_PASSWORD}</password>
25-
</server>
26-
</servers>
27-
<profiles>
28-
<profile>
29-
<!--
30-
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
31-
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
32-
a native Maven with the right version. Eclipse users should points their Maven tooling to
33-
this settings file, or copy the profile into their ~/.m2/settings.xml.
34-
-->
35-
<id>spring</id>
36-
<activation><activeByDefault>true</activeByDefault></activation>
37-
<repositories>
38-
<repository>
39-
<id>spring-snapshots</id>
40-
<name>Spring Snapshots</name>
41-
<url>https://repo.spring.io/libs-snapshot-local</url>
42-
<snapshots>
43-
<enabled>true</enabled>
44-
</snapshots>
45-
</repository>
46-
<repository>
47-
<id>spring-milestones</id>
48-
<name>Spring Milestones</name>
49-
<url>https://repo.spring.io/libs-milestone-local</url>
50-
<snapshots>
51-
<enabled>false</enabled>
52-
</snapshots>
53-
</repository>
54-
<repository>
55-
<id>spring-releases</id>
56-
<name>Spring Releases</name>
57-
<url>https://repo.spring.io/release</url>
58-
<snapshots>
59-
<enabled>false</enabled>
60-
</snapshots>
61-
</repository>
62-
</repositories>
63-
<pluginRepositories>
64-
<pluginRepository>
65-
<id>spring-snapshots</id>
66-
<name>Spring Snapshots</name>
67-
<url>https://repo.spring.io/libs-snapshot-local</url>
68-
<snapshots>
69-
<enabled>true</enabled>
70-
</snapshots>
71-
</pluginRepository>
72-
<pluginRepository>
73-
<id>spring-milestones</id>
74-
<name>Spring Milestones</name>
75-
<url>https://repo.spring.io/libs-milestone-local</url>
76-
<snapshots>
77-
<enabled>false</enabled>
78-
</snapshots>
79-
</pluginRepository>
80-
</pluginRepositories>
81-
</profile>
82-
</profiles>
83-
</settings>
7+
<profiles>
8+
<profile>
9+
<id>spring-community</id>
10+
<activation>
11+
<activeByDefault>true</activeByDefault>
12+
</activation>
13+
<repositories>
14+
<repository>
15+
<id>spring-snapshots</id>
16+
<name>Spring Snapshots</name>
17+
<url>https://repo.spring.io/snapshot</url>
18+
<snapshots>
19+
<enabled>true</enabled>
20+
</snapshots>
21+
</repository>
22+
<repository>
23+
<id>spring-milestones</id>
24+
<name>Spring Milestones</name>
25+
<url>https://repo.spring.io/milestone</url>
26+
<snapshots>
27+
<enabled>false</enabled>
28+
</snapshots>
29+
</repository>
30+
</repositories>
31+
<pluginRepositories>
32+
<pluginRepository>
33+
<id>spring-snapshots</id>
34+
<name>Spring Snapshots</name>
35+
<url>https://repo.spring.io/snapshot</url>
36+
<snapshots>
37+
<enabled>true</enabled>
38+
</snapshots>
39+
</pluginRepository>
40+
</pluginRepositories>
41+
</profile>
42+
</profiles>
43+
44+
<activeProfiles>
45+
<activeProfile>spring-community</activeProfile>
46+
</activeProfiles>
47+
</settings>

spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.cloud.gateway.route;
1818

19+
import java.net.URI;
1920
import java.util.ArrayList;
2021
import java.util.HashMap;
2122
import java.util.LinkedHashMap;
@@ -179,6 +180,42 @@ List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterD
179180
}
180181

181182
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
183+
184+
// --- GH-3293: URI Path Syntactic Sugar ---
185+
URI uri = routeDefinition.getUri();
186+
if (uri != null && org.springframework.util.StringUtils.hasText(uri.getPath()) && !"/".equals(uri.getPath())) {
187+
188+
String path = uri.getPath();
189+
190+
// check if SetPath already exists to avoid duplication
191+
boolean hasSetPath = routeDefinition.getFilters()
192+
.stream()
193+
.anyMatch(f -> "SetPath".equalsIgnoreCase(f.getName()));
194+
195+
if (!hasSetPath) {
196+
// 1. Create the FilterDefinition programmatically
197+
FilterDefinition sugar = new FilterDefinition();
198+
sugar.setName("SetPath");
199+
// SetPath expects a parameter named 'template'
200+
sugar.addArg("template", path);
201+
202+
// Add it at the beginning of the filter chain
203+
routeDefinition.getFilters().add(0, sugar);
204+
205+
// 2. Strip the path from the URI to prevent double-pathing
206+
// (e.g., http://example.com/foo -> http://example.com)
207+
URI strippedUri = org.springframework.web.util.UriComponentsBuilder.fromUri(uri)
208+
.replacePath(null)
209+
.build()
210+
.toUri();
211+
routeDefinition.setUri(strippedUri);
212+
}
213+
}
214+
// --- END GH-3293 ---
215+
216+
// ... existing code follows (List<GatewayFilter> filters = new ArrayList<>();
217+
// etc.)
218+
182219
List<GatewayFilter> filters = new ArrayList<>();
183220
Objects.requireNonNull(routeDefinition.getId(), "Route id must be set");
184221
// TODO: support option to apply defaults after route specific filters?

spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocatorTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,41 @@ public void contextLoadsAndApplyRouteIdToRetryFilter() {
200200
}).expectComplete().verify();
201201
}
202202

203+
@Test
204+
public void uriWithMappingShouldAddSetPathFilter() {
205+
// 1. Setup
206+
List<RoutePredicateFactory> predicates = Arrays.asList(new HostRoutePredicateFactory());
207+
List<GatewayFilterFactory> gatewayFilterFactories = Arrays
208+
.asList(new org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory());
209+
210+
GatewayProperties gatewayProperties = new GatewayProperties();
211+
212+
// Standard way to create the definition
213+
RouteDefinition definition = new RouteDefinition();
214+
definition.setId("sugar_test");
215+
definition.setUri(URI.create("https://example.com/foo/bar"));
216+
definition.setPredicates(Arrays.asList(new PredicateDefinition("Host=*.example.com")));
217+
218+
gatewayProperties.setRoutes(Arrays.asList(definition));
219+
220+
PropertiesRouteDefinitionLocator routeDefinitionLocator = new PropertiesRouteDefinitionLocator(
221+
gatewayProperties);
222+
223+
// 2. Initialize the Locator
224+
RouteDefinitionRouteLocator routeDefinitionRouteLocator = new RouteDefinitionRouteLocator(
225+
new CompositeRouteDefinitionLocator(Flux.just(routeDefinitionLocator)), predicates,
226+
gatewayFilterFactories, gatewayProperties, new ConfigurationService(null, () -> null, () -> null));
227+
228+
// 3. Verify
229+
StepVerifier.create(routeDefinitionRouteLocator.getRoutes()).assertNext(route -> {
230+
assertThat(route.getUri().toString()).isEqualTo("https://example.com:443");
231+
232+
List<GatewayFilter> filters = route.getFilters();
233+
assertThat(filters).hasSize(1);
234+
assertThat(getFilterClassName(filters.get(0))).contains("SetPath");
235+
}).expectComplete().verify();
236+
}
237+
203238
private List<RouteDefinition> containsInvalidRoutes() {
204239
RouteDefinition foo = new RouteDefinition();
205240
foo.setId("foo");

0 commit comments

Comments
 (0)