Skip to content

Commit 25b57d4

Browse files
committed
Merge branch '6.1.x'
2 parents 71927b3 + 76b2d13 commit 25b57d4

File tree

2 files changed

+93
-11
lines changed

2 files changed

+93
-11
lines changed

spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java

+42-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
4141
* {@link org.springframework.util.PathMatcher} otherwise.
4242
*
4343
* @author Rossen Stoyanchev
44+
* @author Stephane Nicoll
4445
* @since 5.3
4546
*/
4647
public abstract class ServletRequestPathUtils {
@@ -186,14 +187,16 @@ public static boolean hasCachedPath(ServletRequest request) {
186187
*/
187188
private static final class ServletRequestPath implements RequestPath {
188189

190+
private final PathElements pathElements;
191+
189192
private final RequestPath requestPath;
190193

191194
private final PathContainer contextPath;
192195

193-
private ServletRequestPath(String rawPath, @Nullable String contextPath, String servletPathPrefix) {
194-
Assert.notNull(servletPathPrefix, "`servletPathPrefix` is required");
195-
this.requestPath = RequestPath.parse(rawPath, contextPath + servletPathPrefix);
196-
this.contextPath = PathContainer.parsePath(StringUtils.hasText(contextPath) ? contextPath : "");
196+
private ServletRequestPath(PathElements pathElements) {
197+
this.pathElements = pathElements;
198+
this.requestPath = pathElements.createRequestPath();
199+
this.contextPath = pathElements.createContextPath();
197200
}
198201

199202
@Override
@@ -218,7 +221,7 @@ public PathContainer pathWithinApplication() {
218221

219222
@Override
220223
public RequestPath modifyContextPath(String contextPath) {
221-
throw new UnsupportedOperationException();
224+
return new ServletRequestPath(this.pathElements.withContextPath(contextPath));
222225
}
223226

224227

@@ -249,7 +252,7 @@ public static RequestPath parse(HttpServletRequest request) {
249252
requestUri = (requestUri != null ? requestUri : request.getRequestURI());
250253
String servletPathPrefix = getServletPathPrefix(request);
251254
return (StringUtils.hasText(servletPathPrefix) ?
252-
new ServletRequestPath(requestUri, request.getContextPath(), servletPathPrefix) :
255+
new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix)) :
253256
RequestPath.parse(requestUri, request.getContextPath()));
254257
}
255258

@@ -265,6 +268,38 @@ private static String getServletPathPrefix(HttpServletRequest request) {
265268
}
266269
return null;
267270
}
271+
272+
record PathElements(String rawPath, @Nullable String contextPath, String servletPathPrefix) {
273+
274+
PathElements {
275+
Assert.notNull(servletPathPrefix, "`servletPathPrefix` is required");
276+
}
277+
278+
private RequestPath createRequestPath() {
279+
return RequestPath.parse(this.rawPath, this.contextPath + this.servletPathPrefix);
280+
}
281+
282+
private PathContainer createContextPath() {
283+
return PathContainer.parsePath(StringUtils.hasText(this.contextPath) ? this.contextPath : "");
284+
}
285+
286+
PathElements withContextPath(String contextPath) {
287+
if (!contextPath.startsWith("/") || contextPath.endsWith("/")) {
288+
throw new IllegalArgumentException("Invalid contextPath '" + contextPath + "': " +
289+
"must start with '/' and not end with '/'");
290+
}
291+
String contextPathToUse = this.servletPathPrefix + contextPath;
292+
if (StringUtils.hasText(this.contextPath())) {
293+
throw new IllegalStateException("Could not change context path to '" + contextPathToUse +
294+
"': a context path is already specified");
295+
}
296+
if (!this.rawPath.startsWith(contextPathToUse)) {
297+
throw new IllegalArgumentException("Invalid contextPath '" + contextPathToUse + "': " +
298+
"must match the start of requestPath: '" + this.rawPath + "'");
299+
}
300+
return new PathElements(this.rawPath, contextPathToUse, "");
301+
}
302+
}
268303
}
269304

270305
}

spring-web/src/test/java/org/springframework/web/util/ServletRequestPathUtilsTests.java

+51-4
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
28+
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
2729

2830
/**
2931
* Tests for {@link ServletRequestPathUtils}.
3032
*
3133
* @author Rossen Stoyanchev
34+
* @author Stephane Nicoll
3235
*/
3336
class ServletRequestPathUtilsTests {
3437

@@ -47,19 +50,63 @@ void parseAndCache() {
4750
testParseAndCache("/app/servlet/a//", "/app", "/servlet", "/a//");
4851
}
4952

53+
@Test
54+
void modifyPathContextWithExistingContextPath() {
55+
RequestPath requestPath = createRequestPath("/app/api/persons/42", "/app", "/api", "/persons/42");
56+
assertThatIllegalStateException().isThrownBy(() -> requestPath.modifyContextPath("/persons"))
57+
.withMessage("Could not change context path to '/api/persons': a context path is already specified");
58+
}
59+
60+
@Test
61+
void modifyPathContextWhenContextPathIsNotInThePath() {
62+
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
63+
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("/something"))
64+
.withMessage("Invalid contextPath '/api/something': " +
65+
"must match the start of requestPath: '/api/persons/42'");
66+
}
67+
68+
@Test
69+
void modifyPathContextReplacesServletPath() {
70+
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
71+
RequestPath updatedRequestPath = requestPath.modifyContextPath("/persons");
72+
assertThat(updatedRequestPath.contextPath().value()).isEqualTo("/api/persons");
73+
assertThat(updatedRequestPath.pathWithinApplication().value()).isEqualTo("/42");
74+
assertThat(updatedRequestPath.value()).isEqualTo("/api/persons/42");
75+
}
76+
77+
@Test
78+
void modifyPathContextWithContextPathNotStartingWithSlash() {
79+
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
80+
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("persons"))
81+
.withMessage("Invalid contextPath 'persons': must start with '/' and not end with '/'");
82+
}
83+
84+
@Test
85+
void modifyPathContextWithContextPathEndingWithSlash() {
86+
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
87+
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("/persons/"))
88+
.withMessage("Invalid contextPath '/persons/': must start with '/' and not end with '/'");
89+
}
90+
5091
private void testParseAndCache(
5192
String requestUri, String contextPath, String servletPath, String pathWithinApplication) {
5293

94+
RequestPath requestPath = createRequestPath(requestUri, contextPath, servletPath, pathWithinApplication);
95+
96+
assertThat(requestPath.contextPath().value()).isEqualTo(contextPath);
97+
assertThat(requestPath.pathWithinApplication().value()).isEqualTo(pathWithinApplication);
98+
}
99+
100+
private static RequestPath createRequestPath(
101+
String requestUri, String contextPath, String servletPath, String pathWithinApplication) {
102+
53103
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
54104
request.setContextPath(contextPath);
55105
request.setServletPath(servletPath);
56106
request.setHttpServletMapping(new MockHttpServletMapping(
57107
pathWithinApplication, contextPath, "myServlet", MappingMatch.PATH));
58108

59-
RequestPath requestPath = ServletRequestPathUtils.parseAndCache(request);
60-
61-
assertThat(requestPath.contextPath().value()).isEqualTo(contextPath);
62-
assertThat(requestPath.pathWithinApplication().value()).isEqualTo(pathWithinApplication);
109+
return ServletRequestPathUtils.parseAndCache(request);
63110
}
64111

65112
}

0 commit comments

Comments
 (0)