From d550d344d55e0b410f517069bb7d6e4cd816617e Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 3 Sep 2020 15:10:36 +0200 Subject: [PATCH] Respect context path in WebMvc.fn & WebFlux.fn This commit makes several changes in both WebMvc.fn as well as WebFlux.fn. - ServerRequest now exposes a RequestPath through requestPath(), and pathContainer() has been deprecated. - The PathPredicate and PathResourceLookupFunction now respects this RequestPath's pathInApplication() in their path-related functionality. - When nesting, the PathPredicate now appends the matched part of the path to the current context path, instead of removing the matched part (which was done previously). This has the same result: the matched part is gone, but now the full path stays the same. Closes gh-25270 --- .../function/server/MockServerRequest.java | 11 ++- .../web/util/pattern/PathPattern.java | 32 ++++++--- .../web/util/pattern/PathPatternTests.java | 3 + .../function/server/DefaultServerRequest.java | 4 +- .../server/PathResourceLookupFunction.java | 2 +- .../function/server/RequestPredicates.java | 69 +++++-------------- .../function/server/ServerRequest.java | 15 +++- .../server/support/ServerRequestWrapper.java | 7 ++ .../server/RequestPredicatesTests.java | 12 +++- .../function/DefaultServerRequest.java | 10 +-- .../function/PathResourceLookupFunction.java | 2 +- .../servlet/function/RequestPredicates.java | 69 +++++-------------- .../web/servlet/function/ServerRequest.java | 16 ++++- .../function/RequestPredicatesTests.java | 7 ++ 14 files changed, 122 insertions(+), 137 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java b/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java index 606cecada7be..26f874aa33dc 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/reactive/function/server/MockServerRequest.java @@ -41,7 +41,6 @@ import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; -import org.springframework.http.server.PathContainer; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; @@ -69,7 +68,7 @@ public final class MockServerRequest implements ServerRequest { private final URI uri; - private final RequestPath pathContainer; + private final RequestPath requestPath; private final MockHeaders headers; @@ -88,7 +87,7 @@ public final class MockServerRequest implements ServerRequest { private final WebSession session; @Nullable - private Principal principal; + private final Principal principal; @Nullable private final InetSocketAddress remoteAddress; @@ -111,7 +110,7 @@ private MockServerRequest(HttpMethod method, URI uri, String contextPath, MockHe this.method = method; this.uri = uri; - this.pathContainer = RequestPath.parse(uri, contextPath); + this.requestPath = RequestPath.parse(uri, contextPath); this.headers = headers; this.cookies = cookies; this.body = body; @@ -148,8 +147,8 @@ public UriBuilder uriBuilder() { } @Override - public PathContainer pathContainer() { - return this.pathContainer; + public RequestPath requestPath() { + return this.requestPath; } @Override diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java index ece992b7a6f7..042d48a78684 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java @@ -249,7 +249,7 @@ else if (!hasLength(pathContainer)) { @Nullable public PathRemainingMatchInfo matchStartOfPath(PathContainer pathContainer) { if (this.head == null) { - return new PathRemainingMatchInfo(pathContainer); + return new PathRemainingMatchInfo(EMPTY_PATH, pathContainer); } else if (!hasLength(pathContainer)) { return null; @@ -262,15 +262,17 @@ else if (!hasLength(pathContainer)) { return null; } else { - PathRemainingMatchInfo info; + PathContainer pathMatched; + PathContainer pathRemaining; if (matchingContext.remainingPathIndex == pathContainer.elements().size()) { - info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getPathMatchResult()); + pathMatched = pathContainer; + pathRemaining = EMPTY_PATH; } else { - info = new PathRemainingMatchInfo(pathContainer.subPath(matchingContext.remainingPathIndex), - matchingContext.getPathMatchResult()); + pathMatched = pathContainer.subPath(0, matchingContext.remainingPathIndex); + pathRemaining = pathContainer.subPath(matchingContext.remainingPathIndex); } - return info; + return new PathRemainingMatchInfo(pathMatched, pathRemaining, matchingContext.getPathMatchResult()); } } @@ -592,20 +594,32 @@ public String toString() { */ public static class PathRemainingMatchInfo { + private final PathContainer pathMatched; + private final PathContainer pathRemaining; private final PathMatchInfo pathMatchInfo; - PathRemainingMatchInfo(PathContainer pathRemaining) { - this(pathRemaining, PathMatchInfo.EMPTY); + PathRemainingMatchInfo(PathContainer pathMatched, PathContainer pathRemaining) { + this(pathMatched, pathRemaining, PathMatchInfo.EMPTY); } - PathRemainingMatchInfo(PathContainer pathRemaining, PathMatchInfo pathMatchInfo) { + PathRemainingMatchInfo(PathContainer pathMatched, PathContainer pathRemaining, + PathMatchInfo pathMatchInfo) { this.pathRemaining = pathRemaining; + this.pathMatched = pathMatched; this.pathMatchInfo = pathMatchInfo; } + /** + * Return the part of a path that was matched by a pattern. + * @since 5.3 + */ + public PathContainer getPathMatched() { + return this.pathMatched; + } + /** * Return the part of a path that was not matched by a pattern. */ diff --git a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java index b90836aab4aa..707809b07d63 100644 --- a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java @@ -557,6 +557,7 @@ public void pathRemainingEnhancements_spr15419() { pp = parse("/{this}/{one}/{here}"); pri = getPathRemaining(pp, "/foo/bar/goo/boo"); assertThat(pri.getPathRemaining().value()).isEqualTo("/boo"); + assertThat(pri.getPathMatched().value()).isEqualTo("/foo/bar/goo"); assertThat(pri.getUriVariables().get("this")).isEqualTo("foo"); assertThat(pri.getUriVariables().get("one")).isEqualTo("bar"); assertThat(pri.getUriVariables().get("here")).isEqualTo("goo"); @@ -564,11 +565,13 @@ public void pathRemainingEnhancements_spr15419() { pp = parse("/aaa/{foo}"); pri = getPathRemaining(pp, "/aaa/bbb"); assertThat(pri.getPathRemaining().value()).isEqualTo(""); + assertThat(pri.getPathMatched().value()).isEqualTo("/aaa/bbb"); assertThat(pri.getUriVariables().get("foo")).isEqualTo("bbb"); pp = parse("/aaa/bbb"); pri = getPathRemaining(pp, "/aaa/bbb"); assertThat(pri.getPathRemaining().value()).isEqualTo(""); + assertThat(pri.getPathMatched().value()).isEqualTo("/aaa/bbb"); assertThat(pri.getUriVariables().size()).isEqualTo(0); pp = parse("/*/{foo}/b*"); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java index c98f151fac26..7e7d8b4bf1d7 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java @@ -43,7 +43,7 @@ import org.springframework.http.MediaType; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; -import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; @@ -123,7 +123,7 @@ public UriBuilder uriBuilder() { } @Override - public PathContainer pathContainer() { + public RequestPath requestPath() { return request().getPath(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index eb4109c22361..e17cca7906e4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -56,7 +56,7 @@ public PathResourceLookupFunction(String pattern, Resource location) { @Override public Mono apply(ServerRequest request) { - PathContainer pathContainer = request.pathContainer(); + PathContainer pathContainer = request.requestPath().pathWithinApplication(); if (!this.pattern.matches(pathContainer)) { return Mono.empty(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java index b7b4174a49da..6b72b545d934 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java @@ -19,7 +19,6 @@ import java.net.InetSocketAddress; import java.net.URI; import java.security.Principal; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -47,6 +46,7 @@ import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; @@ -492,7 +492,7 @@ public PathPatternPredicate(PathPattern pattern) { @Override public boolean test(ServerRequest request) { - PathContainer pathContainer = request.pathContainer(); + PathContainer pathContainer = request.requestPath().pathWithinApplication(); PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer); traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null); if (info != null) { @@ -518,7 +518,7 @@ private static void mergeAttributes(ServerRequest request, Map v @Override public Optional nest(ServerRequest request) { - return Optional.ofNullable(this.pattern.matchStartOfPath(request.pathContainer())) + return Optional.ofNullable(this.pattern.matchStartOfPath(request.requestPath().pathWithinApplication())) .map(info -> new SubPathServerRequestWrapper(request, info, this.pattern)); } @@ -926,17 +926,27 @@ private static class SubPathServerRequestWrapper implements ServerRequest { private final ServerRequest request; - private final PathContainer pathContainer; + private final RequestPath requestPath; private final Map attributes; public SubPathServerRequestWrapper(ServerRequest request, PathPattern.PathRemainingMatchInfo info, PathPattern pattern) { this.request = request; - this.pathContainer = new SubPathContainer(info.getPathRemaining()); + this.requestPath = requestPath(request.requestPath(), info); this.attributes = mergeAttributes(request, info.getUriVariables(), pattern); } + private static RequestPath requestPath(RequestPath original, PathPattern.PathRemainingMatchInfo info) { + StringBuilder contextPath = new StringBuilder(original.contextPath().value()); + contextPath.append(info.getPathMatched().value()); + int length = contextPath.length(); + if (length > 0 && contextPath.charAt(length - 1) == '/') { + contextPath.setLength(length - 1); + } + return original.modifyContextPath(contextPath.toString()); + } + private static Map mergeAttributes(ServerRequest request, Map pathVariables, PathPattern pattern) { Map result = new ConcurrentHashMap<>(request.attributes()); @@ -972,13 +982,8 @@ public UriBuilder uriBuilder() { } @Override - public String path() { - return this.pathContainer.value(); - } - - @Override - public PathContainer pathContainer() { - return this.pathContainer; + public RequestPath requestPath() { + return this.requestPath; } @Override @@ -1089,46 +1094,6 @@ public String toString() { return method() + " " + path(); } - private static class SubPathContainer implements PathContainer { - - private static final PathContainer.Separator SEPARATOR = () -> "/"; - - - private final String value; - - private final List elements; - - public SubPathContainer(PathContainer original) { - this.value = prefixWithSlash(original.value()); - this.elements = prependWithSeparator(original.elements()); - } - - private static String prefixWithSlash(String path) { - if (!path.startsWith("/")) { - path = "/" + path; - } - return path; - } - - private static List prependWithSeparator(List elements) { - List result = new ArrayList<>(elements); - if (result.isEmpty() || !(result.get(0) instanceof Separator)) { - result.add(0, SEPARATOR); - } - return Collections.unmodifiableList(result); - } - - - @Override - public String value() { - return this.value; - } - - @Override - public List elements() { - return this.elements; - } - } } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java index 1aeb4b917ec0..5376269f2417 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerRequest.java @@ -42,6 +42,7 @@ import org.springframework.http.codec.json.Jackson2CodecSupport; import org.springframework.http.codec.multipart.Part; import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -100,14 +101,24 @@ default HttpMethod method() { * Get the request path. */ default String path() { - return uri().getRawPath(); + return requestPath().pathWithinApplication().value(); } /** * Get the request path as a {@code PathContainer}. + * @deprecated as of 5.3, in favor on {@link #requestPath()} */ + @Deprecated default PathContainer pathContainer() { - return PathContainer.parsePath(path()); + return requestPath(); + } + + /** + * Get the request path as a {@code PathContainer}. + * @since 5.3 + */ + default RequestPath requestPath() { + return exchange().getRequest().getPath(); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/ServerRequestWrapper.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/ServerRequestWrapper.java index 9b92d77cd1bc..20127d8a6345 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/ServerRequestWrapper.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/ServerRequestWrapper.java @@ -38,6 +38,7 @@ import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.multipart.Part; import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; @@ -104,10 +105,16 @@ public String path() { } @Override + @Deprecated public PathContainer pathContainer() { return this.delegate.pathContainer(); } + @Override + public RequestPath requestPath() { + return this.delegate.requestPath(); + } + @Override public Headers headers() { return this.delegate.headers(); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java index 3a8536cdf79b..1c65981b6055 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java @@ -29,7 +29,6 @@ import org.springframework.web.testfixture.server.MockServerWebExchange; import org.springframework.web.util.pattern.PathPatternParser; -import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; /** @@ -141,7 +140,7 @@ public void path() { URI uri = URI.create("https://localhost/path"); RequestPredicate predicate = RequestPredicates.path("/p*"); MockServerHttpRequest mockRequest = MockServerHttpRequest.get(uri.toString()).build(); - ServerRequest request = new DefaultServerRequest(MockServerWebExchange.from(mockRequest), emptyList()); + ServerRequest request = new DefaultServerRequest(MockServerWebExchange.from(mockRequest), Collections.emptyList()); assertThat(predicate.test(request)).isTrue(); mockRequest = MockServerHttpRequest.head("https://example.com").build(); @@ -178,6 +177,15 @@ public void pathPredicates() { assertThat(predicate.test(request)).isTrue(); } + @Test + public void pathWithContext() { + RequestPredicate predicate = RequestPredicates.path("/p*"); + MockServerHttpRequest mockRequest = MockServerHttpRequest.get("https://localhost/context/path") + .contextPath("/context").build(); + ServerRequest request = new DefaultServerRequest(MockServerWebExchange.from(mockRequest), Collections.emptyList()); + assertThat(predicate.test(request)).isTrue(); + } + @Test public void headers() { String name = "MyHeader"; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java index 67addf8f16a4..f0fba03886b0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java @@ -51,7 +51,6 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.server.PathContainer; import org.springframework.http.server.RequestPath; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.lang.Nullable; @@ -132,13 +131,8 @@ public UriBuilder uriBuilder() { } @Override - public String path() { - return pathContainer().value(); - } - - @Override - public PathContainer pathContainer() { - return this.requestPath.pathWithinApplication(); + public RequestPath requestPath() { + return this.requestPath; } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index d1965518add9..7079290f30a0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -55,7 +55,7 @@ public PathResourceLookupFunction(String pattern, Resource location) { @Override public Optional apply(ServerRequest request) { - PathContainer pathContainer = request.pathContainer(); + PathContainer pathContainer = request.requestPath().pathWithinApplication(); if (!this.pattern.matches(pathContainer)) { return Optional.empty(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java index 0b33c63b8d2a..a83a40d618e4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java @@ -21,7 +21,6 @@ import java.net.URI; import java.security.Principal; import java.time.Instant; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -51,6 +50,7 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -490,7 +490,7 @@ public PathPatternPredicate(PathPattern pattern) { @Override public boolean test(ServerRequest request) { - PathContainer pathContainer = request.pathContainer(); + PathContainer pathContainer = request.requestPath().pathWithinApplication(); PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer); traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null); if (info != null) { @@ -516,7 +516,7 @@ private static void mergeAttributes(ServerRequest request, Map v @Override public Optional nest(ServerRequest request) { - return Optional.ofNullable(this.pattern.matchStartOfPath(request.pathContainer())) + return Optional.ofNullable(this.pattern.matchStartOfPath(request.requestPath().pathWithinApplication())) .map(info -> new SubPathServerRequestWrapper(request, info, this.pattern)); } @@ -924,17 +924,27 @@ private static class SubPathServerRequestWrapper implements ServerRequest { private final ServerRequest request; - private final PathContainer pathContainer; + private RequestPath requestPath; private final Map attributes; public SubPathServerRequestWrapper(ServerRequest request, PathPattern.PathRemainingMatchInfo info, PathPattern pattern) { this.request = request; - this.pathContainer = new SubPathContainer(info.getPathRemaining()); + this.requestPath = requestPath(request.requestPath(), info); this.attributes = mergeAttributes(request, info.getUriVariables(), pattern); } + private static RequestPath requestPath(RequestPath original, PathPattern.PathRemainingMatchInfo info) { + StringBuilder contextPath = new StringBuilder(original.contextPath().value()); + contextPath.append(info.getPathMatched().value()); + int length = contextPath.length(); + if (length > 0 && contextPath.charAt(length - 1) == '/') { + contextPath.setLength(length - 1); + } + return original.modifyContextPath(contextPath.toString()); + } + private static Map mergeAttributes(ServerRequest request, Map pathVariables, PathPattern pattern) { Map result = new ConcurrentHashMap<>(request.attributes()); @@ -970,13 +980,8 @@ public UriBuilder uriBuilder() { } @Override - public String path() { - return this.pathContainer.value(); - } - - @Override - public PathContainer pathContainer() { - return this.pathContainer; + public RequestPath requestPath() { + return this.requestPath; } @Override @@ -1084,46 +1089,6 @@ public String toString() { return method() + " " + path(); } - private static class SubPathContainer implements PathContainer { - - private static final PathContainer.Separator SEPARATOR = () -> "/"; - - - private final String value; - - private final List elements; - - public SubPathContainer(PathContainer original) { - this.value = prefixWithSlash(original.value()); - this.elements = prependWithSeparator(original.elements()); - } - - private static String prefixWithSlash(String path) { - if (!path.startsWith("/")) { - path = "/" + path; - } - return path; - } - - private static List prependWithSeparator(List elements) { - List result = new ArrayList<>(elements); - if (result.isEmpty() || !(result.get(0) instanceof Separator)) { - result.add(0, SEPARATOR); - } - return Collections.unmodifiableList(result); - } - - - @Override - public String value() { - return this.value; - } - - @Override - public List elements() { - return this.elements; - } - } } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerRequest.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerRequest.java index a76a1cab4202..00beac0420c8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerRequest.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerRequest.java @@ -43,10 +43,12 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; +import org.springframework.web.util.ServletRequestPathUtils; import org.springframework.web.util.UriBuilder; /** @@ -92,14 +94,24 @@ default HttpMethod method() { * Get the request path. */ default String path() { - return uri().getRawPath(); + return requestPath().pathWithinApplication().value(); } /** * Get the request path as a {@code PathContainer}. + * @deprecated as of 5.3, in favor on {@link #requestPath()} */ + @Deprecated default PathContainer pathContainer() { - return PathContainer.parsePath(path()); + return requestPath(); + } + + /** + * Get the request path as a {@code PathContainer}. + * @since 5.3 + */ + default RequestPath requestPath() { + return ServletRequestPathUtils.getParsedRequestPath(servletRequest()); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/function/RequestPredicatesTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/function/RequestPredicatesTests.java index 1cf7a57f0b0b..f030776ef905 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/function/RequestPredicatesTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/function/RequestPredicatesTests.java @@ -148,6 +148,13 @@ void pathPredicates() { assertThat(predicate.test(initRequest("GET", "/path"))).isTrue(); } + @Test + public void pathWithContext() { + RequestPredicate predicate = RequestPredicates.path("/p*"); + ServerRequest request = initRequest("GET", "/context/path", + servletRequest -> servletRequest.setContextPath("/context")); + assertThat(predicate.test(request)).isTrue(); + } @Test void headers() {