diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java index 3cf2d29f04..2aadbbee86 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java @@ -69,7 +69,7 @@ class DefaultRequestPath implements RequestPath { counter += ((Segment) element).semicolonContent().length(); } if (length == counter) { - return DefaultPathContainer.subPath(path, 0, i + 1); + return path.subPath(0, i + 1); } } @@ -79,7 +79,7 @@ class DefaultRequestPath implements RequestPath { } private static PathContainer extractPathWithinApplication(PathContainer fullPath, PathContainer contextPath) { - return PathContainer.subPath(fullPath, contextPath.elements().size()); + return fullPath.subPath(contextPath.elements().size()); } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/PathContainer.java b/spring-web/src/main/java/org/springframework/http/server/reactive/PathContainer.java index c6d00dc4eb..88045eed40 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/PathContainer.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/PathContainer.java @@ -22,16 +22,21 @@ import java.util.List; import org.springframework.util.MultiValueMap; /** - * Structured path representation. + * Structured representation of a path whose {@link Element Elements} are + * accessible as a sequence of either {@link Separator Separator} and/or + * {@link Segment Segment} (element) types. * - *

Typically consumed via {@link ServerHttpRequest#getPath()} but can also - * be created by parsing a path value via {@link #parse(String, Charset)}. + *

Each {@code Segment} exposes its own structure decoded safely without the + * risk of encoded reserved characters altering the path or segment structure. + * + *

An instance of this class can also be created via + * {@link #parse(String, Charset)}. The path for an HTTP request is parsed once + * and subsequently accessible via {@link ServerHttpRequest#getPath()}. * * @author Rossen Stoyanchev */ public interface PathContainer { - /** * The original, raw (encoded) path value including path parameters. */ @@ -42,6 +47,26 @@ public interface PathContainer { */ List elements(); + /** + * Extract a sub-path from the given offset into the elements list. + * @param index the start element index (inclusive) + * @return the sub-path + */ + default PathContainer subPath(int index) { + return subPath(index, elements().size()); + } + + /** + * Extract a sub-path from the given start offset (inclusive) into the + * element list and to the end offset (exclusive). + * @param startIndex the start element index (inclusive) + * @param endIndex the end element index (exclusive) + * @return the sub-path + */ + default PathContainer subPath(int startIndex, int endIndex) { + return DefaultPathContainer.subPath(this, startIndex, endIndex); + } + /** * Parse the given path value into a {@link PathContainer}. @@ -53,29 +78,10 @@ public interface PathContainer { return DefaultPathContainer.parsePath(path, encoding); } - /** - * Extract a sub-path from the given offset into the path elements list. - * @param path the path to extract from - * @param index the start element index (inclusive) - * @return the sub-path - */ - static PathContainer subPath(PathContainer path, int index) { - return subPath(path, index, path.elements().size()); - } /** - * Extract a sub-path from the given start offset (inclusive) into the path - * element list and to the end offset (exclusive). - * @param path the path to extract from - * @param startIndex the start element index (inclusive) - * @param endIndex the end element index (exclusive) - * @return the sub-path + * Common representation of a path element, e.g. separator or segment. */ - static PathContainer subPath(PathContainer path, int startIndex, int endIndex) { - return DefaultPathContainer.subPath(path, startIndex, endIndex); - } - - interface Element { /** @@ -86,14 +92,14 @@ public interface PathContainer { /** - * A path separator element. + * Path separator element. */ interface Separator extends Element { } /** - * A path segment element. + * Path segment element. */ interface Segment extends Element { 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 3e103fa0cf..51ff839eee 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 @@ -205,7 +205,7 @@ public class PathPattern implements Comparable { info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getPathMatchResult()); } else { - info = new PathRemainingMatchInfo(PathContainer.subPath(pathContainer, matchingContext.remainingPathIndex), + info = new PathRemainingMatchInfo(pathContainer.subPath(matchingContext.remainingPathIndex), matchingContext.getPathMatchResult()); } return info; diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathContainerTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathContainerTests.java index 3905478157..dab1a246ad 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathContainerTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathContainerTests.java @@ -127,17 +127,17 @@ public class DefaultPathContainerTests { public void subPath() throws Exception { // basic PathContainer path = PathContainer.parse("/a/b/c", UTF_8); - assertSame(path, PathContainer.subPath(path, 0)); - assertEquals("/b/c", PathContainer.subPath(path, 2).value()); - assertEquals("/c", PathContainer.subPath(path, 4).value()); + assertSame(path, path.subPath(0)); + assertEquals("/b/c", path.subPath(2).value()); + assertEquals("/c", path.subPath(4).value()); // root path path = PathContainer.parse("/", UTF_8); - assertEquals("/", PathContainer.subPath(path, 0).value()); + assertEquals("/", path.subPath(0).value()); // trailing slash path = PathContainer.parse("/a/b/", UTF_8); - assertEquals("/b/", PathContainer.subPath(path, 2).value()); + assertEquals("/b/", path.subPath(2).value()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java index 536d76901f..8542c0e336 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java @@ -215,7 +215,7 @@ public class ResourceUrlProvider implements ApplicationListener { PathContainer path = entry.getKey().extractPathWithinPattern(lookupPath); int endIndex = lookupPath.elements().size() - path.elements().size(); - PathContainer mapping = PathContainer.subPath(lookupPath, 0, endIndex); + PathContainer mapping = lookupPath.subPath(0, endIndex); if (logger.isTraceEnabled()) { logger.trace("Invoking ResourceResolverChain for URL pattern " + "\"" + entry.getKey() + "\"");