diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java index 9bac52b388e..30ff6ff55c5 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.web.util.pattern; import org.springframework.http.server.PathContainer; +import org.springframework.util.StringUtils; /** * Parser for URI path patterns producing {@link PathPattern} instances that can @@ -103,6 +104,17 @@ public class PathPatternParser { } + /** + * Prepare the given pattern for use in matching to full URL paths. + *

By default, prepend a leading slash if needed for non-empty patterns. + * @param pattern the pattern to initialize + * @return the updated pattern + * @since 5.2.25 + */ + public String initFullPathPattern(String pattern) { + return (StringUtils.hasLength(pattern) && !pattern.startsWith("/") ? "/" + pattern : pattern); + } + /** * Process the path pattern content, a character at a time, breaking it into * path elements around separator boundaries and verifying the structure at each 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 003c1571eb1..c43bfbb1f55 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -110,10 +110,9 @@ public abstract class RequestPredicates { */ public static RequestPredicate path(String pattern) { Assert.notNull(pattern, "'pattern' must not be null"); - if (!pattern.isEmpty() && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - return pathPredicates(PathPatternParser.defaultInstance).apply(pattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + pattern = parser.initFullPathPattern(pattern); + return pathPredicates(parser).apply(pattern); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java index a4997b3ab8f..71eb1865f0b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java @@ -29,10 +29,10 @@ import org.springframework.beans.BeansException; import org.springframework.http.server.PathContainer; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import org.springframework.web.filter.reactive.ServerHttpObservationFilter; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; /** * Abstract base class for URL-mapped @@ -213,8 +213,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { Object resolvedHandler = handler; // Parse path pattern - urlPath = prependLeadingSlash(urlPath); - PathPattern pattern = getPathPatternParser().parse(urlPath); + PathPatternParser parser = getPathPatternParser(); + urlPath = parser.initFullPathPattern(urlPath); + PathPattern pattern = parser.parse(urlPath); if (this.handlerMap.containsKey(pattern)) { Object existingHandler = this.handlerMap.get(pattern); if (existingHandler != null && existingHandler != resolvedHandler) { @@ -242,14 +243,4 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { return (handler instanceof String ? "'" + handler + "'" : handler.toString()); } - - private static String prependLeadingSlash(String pattern) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - return "/" + pattern; - } - else { - return pattern; - } - } - } 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 8f3e3b0bbf0..765e021fc68 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,6 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.http.server.PathContainer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; @@ -86,8 +85,9 @@ public class ResourceUrlProvider implements ApplicationListener handlerMap) { this.handlerMap.clear(); handlerMap.forEach((rawPattern, resourceWebHandler) -> { - rawPattern = prependLeadingSlash(rawPattern); - PathPattern pattern = PathPatternParser.defaultInstance.parse(rawPattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + rawPattern = parser.initFullPathPattern(rawPattern); + PathPattern pattern = parser.parse(rawPattern); this.handlerMap.put(pattern, resourceWebHandler); }); } @@ -172,14 +172,4 @@ public class ResourceUrlProvider implements ApplicationListener result = new ArrayList<>(patterns.length); - for (String path : patterns) { - if (StringUtils.hasText(path) && !path.startsWith("/")) { - path = "/" + path; - } - result.add(parser.parse(path)); + for (String pattern : patterns) { + pattern = parser.initFullPathPattern(pattern); + result.add(parser.parse(pattern)); } return result; } 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 08606f5c01f..b71277ed16d 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,10 +109,9 @@ public abstract class RequestPredicates { */ public static RequestPredicate path(String pattern) { Assert.notNull(pattern, "'pattern' must not be null"); - if (!pattern.isEmpty() && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - return pathPredicates(PathPatternParser.defaultInstance).apply(pattern); + PathPatternParser parser = PathPatternParser.defaultInstance; + pattern = parser.initFullPathPattern(pattern); + return pathPredicates(parser).apply(pattern); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java index 623c42d8e34..6c194b88d83 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerMappingIntrospector.java @@ -51,6 +51,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.util.ServletRequestPathUtils; import org.springframework.web.util.UrlPathHelper; +import org.springframework.web.util.pattern.PathPatternParser; /** * Helper class to get information from the {@code HandlerMapping} that would @@ -309,10 +310,15 @@ public class HandlerMappingIntrospector ServletRequestPathUtils.PATH_ATTRIBUTE : UrlPathHelper.PATH_ATTRIBUTE); } + @Override + public PathPatternParser getPatternParser() { + return this.delegate.getPatternParser(); + } + @Nullable @Override public RequestMatchResult match(HttpServletRequest request, String pattern) { - pattern = (StringUtils.hasLength(pattern) && !pattern.startsWith("/") ? "/" + pattern : pattern); + pattern = initFullPathPattern(pattern); Object previousPath = request.getAttribute(this.pathAttributeName); request.setAttribute(this.pathAttributeName, this.lookupPath); try { @@ -323,6 +329,11 @@ public class HandlerMappingIntrospector } } + private String initFullPathPattern(String pattern) { + PathPatternParser parser = (getPatternParser() != null ? getPatternParser() : PathPatternParser.defaultInstance); + return parser.initFullPathPattern(pattern); + } + @Nullable @Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PathPatternsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PathPatternsRequestCondition.java index 93e9033fa8c..091ea4e53a5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PathPatternsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PathPatternsRequestCondition.java @@ -79,11 +79,9 @@ public final class PathPatternsRequestCondition extends AbstractRequestCondition return EMPTY_PATH_PATTERN; } SortedSet result = new TreeSet<>(); - for (String path : patterns) { - if (StringUtils.hasText(path) && !path.startsWith("/")) { - path = "/" + path; - } - result.add(parser.parse(path)); + for (String pattern : patterns) { + pattern = parser.initFullPathPattern(pattern); + result.add(parser.parse(pattern)); } return result; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java index f9b69ffbdb3..ea28749fe6a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java @@ -35,6 +35,7 @@ import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; /** * A logical disjunction (' || ') request condition that matches a request @@ -158,9 +159,7 @@ public class PatternsRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(patterns.length); for (String pattern : patterns) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } + pattern = PathPatternParser.defaultInstance.initFullPathPattern(pattern); result.add(pattern); } return result; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index 839b51d51d3..ecaa081426a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -64,6 +64,7 @@ import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.pattern.PathPatternParser; /** * Creates instances of {@link org.springframework.web.util.UriComponentsBuilder} @@ -544,10 +545,8 @@ public class MvcUriComponentsBuilder { String typePath = getClassMapping(controllerType); String methodPath = getMethodMapping(method); String path = pathMatcher.combine(typePath, methodPath); - if (StringUtils.hasLength(path) && !path.startsWith("/")) { - path = "/" + path; - } - else if (!StringUtils.hasText(prefix + path)) { + path = PathPatternParser.defaultInstance.initFullPathPattern(path); + if (!StringUtils.hasText(prefix + path)) { path = "/"; } builder.path(path);