diff --git a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
index f1eaa47e25a8..35268a886d66 100644
--- a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
+++ b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
@@ -24,8 +24,8 @@
import org.springframework.util.PathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.pattern.ParsingPathMatcher;
+import org.springframework.web.server.support.LookupPath;
/**
* Provide a per reactive request {@link CorsConfiguration} instance based on a
@@ -43,8 +43,6 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
private PathMatcher pathMatcher = new ParsingPathMatcher();
- private HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
-
/**
* Set the PathMatcher implementation to use for matching URL paths
@@ -56,26 +54,6 @@ public void setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
}
- /**
- * Set if context path and request URI should be URL-decoded. Both are returned
- * undecoded by the Servlet API, in contrast to the servlet path.
- *
Uses either the request encoding or the default encoding according
- * to the Servlet spec (ISO-8859-1).
- * @see HttpRequestPathHelper#setUrlDecode
- */
- public void setUrlDecode(boolean urlDecode) {
- this.pathHelper.setUrlDecode(urlDecode);
- }
-
- /**
- * Set the UrlPathHelper to use for resolution of lookup paths.
- *
Use this to override the default UrlPathHelper with a custom subclass.
- */
- public void setHttpRequestPathHelper(HttpRequestPathHelper pathHelper) {
- Assert.notNull(pathHelper, "HttpRequestPathHelper must not be null");
- this.pathHelper = pathHelper;
- }
-
/**
* Set CORS configuration based on URL patterns.
*/
@@ -102,7 +80,7 @@ public void registerCorsConfiguration(String path, CorsConfiguration config) {
@Override
public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) {
- String lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
+ String lookupPath = exchange.getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE).get().getPath();
for (Map.Entry entry : this.corsConfigurations.entrySet()) {
if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
return entry.getValue();
diff --git a/spring-web/src/main/java/org/springframework/web/server/support/HttpRequestPathHelper.java b/spring-web/src/main/java/org/springframework/web/server/support/HttpRequestPathHelper.java
index ac92db6a69a6..3842184adece 100644
--- a/spring-web/src/main/java/org/springframework/web/server/support/HttpRequestPathHelper.java
+++ b/spring-web/src/main/java/org/springframework/web/server/support/HttpRequestPathHelper.java
@@ -57,9 +57,14 @@ public boolean shouldUrlDecode() {
}
- public String getLookupPathForRequest(ServerWebExchange exchange) {
+ public LookupPath getLookupPathForRequest(ServerWebExchange exchange) {
String path = getPathWithinApplication(exchange.getRequest());
- return (shouldUrlDecode() ? decode(exchange, path) : path);
+ path = (shouldUrlDecode() ? decode(exchange, path) : path);
+ int begin = path.lastIndexOf('/') + 1;
+ int end = path.length();
+ int paramIndex = path.indexOf(';', begin);
+ int extIndex = path.lastIndexOf('.', paramIndex != -1 ? paramIndex : end);
+ return new LookupPath(path, extIndex, paramIndex);
}
private String getPathWithinApplication(ServerHttpRequest request) {
diff --git a/spring-web/src/main/java/org/springframework/web/server/support/LookupPath.java b/spring-web/src/main/java/org/springframework/web/server/support/LookupPath.java
new file mode 100644
index 000000000000..3fc00e0970c6
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/server/support/LookupPath.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.server.support;
+
+import org.springframework.lang.Nullable;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * Lookup path information of an incoming HTTP request.
+ *
+ * @author Brian Clozel
+ * @since 5.0
+ * @see HttpRequestPathHelper
+ */
+public final class LookupPath {
+
+ public static final String LOOKUP_PATH_ATTRIBUTE = LookupPath.class.getName();
+
+ private final String path;
+
+ private final int fileExtensionIndex;
+
+ private final int pathParametersIndex;
+
+ public LookupPath(String path, int fileExtensionIndex, int pathParametersIndex) {
+ this.path = path;
+ this.fileExtensionIndex = fileExtensionIndex;
+ this.pathParametersIndex = pathParametersIndex;
+ }
+
+ public String getPath() {
+ if (this.pathParametersIndex != -1) {
+ // TODO: variant without the path parameter information?
+ //return this.path.substring(0, this.pathParametersIndex);
+ return this.path;
+ }
+ else {
+ return this.path;
+ }
+ }
+
+ public String getPathWithoutExtension() {
+ if (this.fileExtensionIndex != -1) {
+ return this.path.substring(0, this.fileExtensionIndex);
+ }
+ else {
+ return this.path;
+ }
+ }
+
+ @Nullable
+ public String getFileExtension() {
+ if (this.fileExtensionIndex == -1) {
+ return null;
+ }
+ else if (this.pathParametersIndex == -1) {
+ return this.path.substring(this.fileExtensionIndex);
+ }
+ else {
+ return this.path.substring(this.fileExtensionIndex, this.pathParametersIndex);
+ }
+ }
+
+ @Nullable
+ public String getPathParameters() {
+ return this.pathParametersIndex == -1 ?
+ null : this.path.substring(this.pathParametersIndex + 1);
+ }
+
+}
diff --git a/spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java b/spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java
index 75775e790e2d..4fe628ed9473 100644
--- a/spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java
+++ b/spring-web/src/test/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSourceTests.java
@@ -21,6 +21,8 @@
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -39,6 +41,7 @@ public class UrlBasedCorsConfigurationSourceTests {
@Test
public void empty() {
ServerWebExchange exchange = MockServerHttpRequest.get("/bar/test.html").toExchange();
+ setLookupPathAttribute(exchange);
assertNull(this.configSource.getCorsConfiguration(exchange));
}
@@ -48,9 +51,11 @@ public void registerAndMatch() {
this.configSource.registerCorsConfiguration("/bar/**", config);
ServerWebExchange exchange = MockServerHttpRequest.get("/foo/test.html").toExchange();
+ setLookupPathAttribute(exchange);
assertNull(this.configSource.getCorsConfiguration(exchange));
exchange = MockServerHttpRequest.get("/bar/test.html").toExchange();
+ setLookupPathAttribute(exchange);
assertEquals(config, this.configSource.getCorsConfiguration(exchange));
}
@@ -59,4 +64,10 @@ public void unmodifiableConfigurationsMap() {
this.configSource.getCorsConfigurations().put("/**", new CorsConfiguration());
}
+ public void setLookupPathAttribute(ServerWebExchange exchange) {
+ HttpRequestPathHelper helper = new HttpRequestPathHelper();
+ exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE,
+ helper.getLookupPathForRequest(exchange));
+ }
+
}
diff --git a/spring-web/src/test/java/org/springframework/web/server/support/LookupPathTests.java b/spring-web/src/test/java/org/springframework/web/server/support/LookupPathTests.java
new file mode 100644
index 000000000000..11a46dd12a07
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/web/server/support/LookupPathTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.server.support;
+
+import org.junit.Test;
+
+import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for {@link LookupPath}
+ * @author Brian Clozel
+ */
+public class LookupPathTests {
+
+ @Test
+ public void parsePath() {
+ LookupPath path = create("/foo");
+ assertEquals("/foo", path.getPath());
+ assertEquals("/foo", path.getPathWithoutExtension());
+ }
+
+ @Test
+ public void parsePathWithExtension() {
+ LookupPath path = create("/foo.txt");
+ assertEquals("/foo.txt", path.getPath());
+ assertEquals("/foo", path.getPathWithoutExtension());
+ assertEquals(".txt", path.getFileExtension());
+ }
+
+ @Test
+ public void parsePathWithParams() {
+ LookupPath path = create("/test/foo.txt;foo=bar?framework=spring");
+ assertEquals("/test/foo.txt;foo=bar", path.getPath());
+ assertEquals("/test/foo", path.getPathWithoutExtension());
+ assertEquals(".txt", path.getFileExtension());
+ assertEquals("foo=bar", path.getPathParameters());
+ }
+
+ private LookupPath create(String path) {
+ HttpRequestPathHelper helper = new HttpRequestPathHelper();
+ ServerWebExchange exchange = MockServerHttpRequest.get(path).build().toExchange();
+ return helper.getLookupPathForRequest(exchange);
+ }
+}
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java
index d2a20b0befab..92d8937abb30 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java
@@ -17,6 +17,7 @@
package org.springframework.web.reactive.handler;
import java.util.Map;
+import java.util.Optional;
import reactor.core.publisher.Mono;
@@ -32,6 +33,7 @@
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.reactive.HandlerMapping;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.support.HttpRequestPathHelper;
@@ -43,6 +45,7 @@
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
+ * @author Brian Clozel
* @since 5.0
*/
public abstract class AbstractHandlerMapping extends ApplicationObjectSupport implements HandlerMapping, Ordered {
@@ -171,6 +174,19 @@ public Mono getHandler(ServerWebExchange exchange) {
});
}
+ protected LookupPath getLookupPath(ServerWebExchange exchange) {
+ Optional attribute = exchange.getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE);
+ return attribute.orElseGet(() -> {
+ LookupPath lookupPath = createLookupPath(exchange);
+ exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE, lookupPath);
+ return lookupPath;
+ });
+ }
+
+ protected LookupPath createLookupPath(ServerWebExchange exchange) {
+ return getPathHelper().getLookupPathForRequest(exchange);
+ }
+
/**
* Look up a handler for the given request, returning an empty {@code Mono}
* if no specific one is found. This method is called by {@link #getHandler}.
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 4458bb34c631..c353b4376a90 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
@@ -28,6 +28,7 @@
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.server.ServerWebExchange;
/**
@@ -46,6 +47,7 @@
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
+ * @author Brian Clozel
* @since 5.0
*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
@@ -99,7 +101,7 @@ public final Map getHandlerMap() {
@Override
public Mono getHandlerInternal(ServerWebExchange exchange) {
- String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
+ LookupPath lookupPath = getLookupPath(exchange);
Object handler;
try {
handler = lookupHandler(lookupPath, exchange);
@@ -109,30 +111,31 @@ public Mono getHandlerInternal(ServerWebExchange exchange) {
}
if (handler != null && logger.isDebugEnabled()) {
- logger.debug("Mapping [" + lookupPath + "] to " + handler);
+ logger.debug("Mapping [" + lookupPath.getPath() + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
- logger.trace("No handler mapping found for [" + lookupPath + "]");
+ logger.trace("No handler mapping found for [" + lookupPath.getPath() + "]");
}
return Mono.justOrEmpty(handler);
}
/**
- * Look up a handler instance for the given URL path.
+ * Look up a handler instance for the given URL lookup path.
+ *
* Supports direct matches, e.g. a registered "/test" matches "/test",
- * and various Ant-style pattern matches, e.g. a registered "/t*" matches
- * both "/test" and "/team". For details, see the AntPathMatcher class.
- *
Looks for the most exact pattern, where most exact is defined as
- * the longest path pattern.
- * @param urlPath URL the bean is mapped to
+ * and various path pattern matches, e.g. a registered "/t*" matches
+ * both "/test" and "/team". For details, see the PathPattern class.
+ *
+ * @param lookupPath URL the handler is mapped to
* @param exchange the current exchange
* @return the associated handler instance, or {@code null} if not found
* @see org.springframework.web.util.pattern.ParsingPathMatcher
*/
@Nullable
- protected Object lookupHandler(String urlPath, ServerWebExchange exchange) throws Exception {
+ protected Object lookupHandler(LookupPath lookupPath, ServerWebExchange exchange) throws Exception {
// Direct match?
+ String urlPath = lookupPath.getPath();
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
return handleMatch(handler, urlPath, urlPath, exchange);
@@ -156,7 +159,7 @@ else if (useTrailingSlashMatch()) {
if (!matches.isEmpty()) {
Collections.sort(matches, comparator);
if (logger.isDebugEnabled()) {
- logger.debug("Matching patterns for request [" + urlPath + "] are " + matches);
+ logger.debug("Matching patterns for request [" + lookupPath + "] are " + matches);
}
bestMatch = matches.get(0);
}
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 b3ef7234bd57..40c822c0a8e3 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
@@ -37,6 +37,7 @@
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.util.pattern.ParsingPathMatcher;
/**
@@ -184,8 +185,8 @@ public final Mono getForRequestUrl(ServerWebExchange exchange, String re
private int getLookupPathIndex(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
String requestPath = request.getURI().getPath();
- String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
- return requestPath.indexOf(lookupPath);
+ LookupPath lookupPath = getPathHelper().getLookupPathForRequest(exchange);
+ return requestPath.indexOf(lookupPath.getPath());
}
private int getEndPathIndex(String lookupPath) {
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java
index 3814cec17cb8..6274e96ee2c7 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java
@@ -29,8 +29,8 @@
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.pattern.ParsingPathMatcher;
/**
@@ -44,8 +44,6 @@ public final class PatternsRequestCondition extends AbstractRequestCondition patterns;
- private final HttpRequestPathHelper pathHelper;
-
private final PathMatcher pathMatcher;
private final boolean useSuffixPatternMatch;
@@ -61,35 +59,33 @@ public final class PatternsRequestCondition extends AbstractRequestCondition extensions) {
- this(asList(patterns), pathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, extensions);
+ this(asList(patterns), pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, extensions);
}
/**
* Private constructor accepting a collection of patterns.
*/
- private PatternsRequestCondition(Collection patterns, HttpRequestPathHelper pathHelper,
- PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
+ private PatternsRequestCondition(Collection patterns, PathMatcher pathMatcher,
+ boolean useSuffixPatternMatch, boolean useTrailingSlashMatch,
Set fileExtensions) {
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
- this.pathHelper = (pathHelper != null ? pathHelper : new HttpRequestPathHelper());
this.pathMatcher = (pathMatcher != null ? pathMatcher : new ParsingPathMatcher());
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.useTrailingSlashMatch = useTrailingSlashMatch;
@@ -165,7 +161,7 @@ else if (!other.patterns.isEmpty()) {
else {
result.add("");
}
- return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
+ return new PatternsRequestCondition(result, this.pathMatcher, this.useSuffixPatternMatch,
this.useTrailingSlashMatch, this.fileExtensions);
}
@@ -191,12 +187,13 @@ public PatternsRequestCondition getMatchingCondition(ServerWebExchange exchange)
return this;
}
- String lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
+ LookupPath lookupPath = exchange
+ .getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE).get();
List matches = getMatchingPatterns(lookupPath);
return matches.isEmpty() ? null :
- new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
- this.useTrailingSlashMatch, this.fileExtensions);
+ new PatternsRequestCondition(matches, this.pathMatcher, this.useSuffixPatternMatch,
+ this.useTrailingSlashMatch, this.fileExtensions);
}
/**
@@ -208,7 +205,7 @@ public PatternsRequestCondition getMatchingCondition(ServerWebExchange exchange)
* @param lookupPath the lookup path to match to existing patterns
* @return a collection of matching patterns sorted with the closest match at the top
*/
- public List getMatchingPatterns(String lookupPath) {
+ public List getMatchingPatterns(LookupPath lookupPath) {
List matches = new ArrayList<>();
for (String pattern : this.patterns) {
String match = getMatchingPattern(pattern, lookupPath);
@@ -216,34 +213,33 @@ public List getMatchingPatterns(String lookupPath) {
matches.add(match);
}
}
- Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
+ Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath.getPath()));
return matches;
}
- private String getMatchingPattern(String pattern, String lookupPath) {
- if (pattern.equals(lookupPath)) {
+ private String getMatchingPattern(String pattern, LookupPath lookupPath) {
+ if (pattern.equals(lookupPath.getPath())) {
return pattern;
}
if (this.useSuffixPatternMatch) {
- if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
- for (String extension : this.fileExtensions) {
- if (this.pathMatcher.match(pattern + extension, lookupPath)) {
- return pattern + extension;
- }
+ if (!this.fileExtensions.isEmpty() && lookupPath.getFileExtension() != null) {
+ if (this.fileExtensions.contains(lookupPath.getFileExtension()) &&
+ this.pathMatcher.match(pattern, lookupPath.getPathWithoutExtension())) {
+ return pattern + lookupPath.getFileExtension();
}
}
else {
- boolean hasSuffix = pattern.indexOf('.') != -1;
- if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
+ if (lookupPath.getFileExtension() != null
+ && this.pathMatcher.match(pattern , lookupPath.getPathWithoutExtension())) {
return pattern + ".*";
}
}
}
- if (this.pathMatcher.match(pattern, lookupPath)) {
+ if (this.pathMatcher.match(pattern, lookupPath.getPath())) {
return pattern;
}
if (this.useTrailingSlashMatch) {
- if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
+ if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath.getPath())) {
return pattern +"/";
}
}
@@ -263,8 +259,9 @@ private String getMatchingPattern(String pattern, String lookupPath) {
*/
@Override
public int compareTo(PatternsRequestCondition other, ServerWebExchange exchange) {
- String lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
- Comparator patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
+ LookupPath lookupPath = exchange
+ .getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE).get();
+ Comparator patternComparator = this.pathMatcher.getPatternComparator(lookupPath.getPath());
Iterator iterator = this.patterns.iterator();
Iterator iteratorOther = other.patterns.iterator();
while (iterator.hasNext() && iteratorOther.hasNext()) {
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
index 9585e19b1884..0b6862820203 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java
@@ -45,6 +45,7 @@
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.support.LookupPath;
/**
* Abstract base class for {@link HandlerMapping} implementations that define
@@ -54,6 +55,7 @@
* subclasses defining the details of the mapping type {@code }.
*
* @author Rossen Stoyanchev
+ * @author Brian Clozel
* @since 5.0
* @param The mapping for a {@link HandlerMethod} containing the conditions
* needed to match the handler method to incoming request.
@@ -255,7 +257,7 @@ protected void handlerMethodsInitialized(Map handlerMethods) {
*/
@Override
public Mono getHandlerInternal(ServerWebExchange exchange) {
- String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
+ LookupPath lookupPath = getLookupPath(exchange);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
@@ -289,18 +291,18 @@ public Mono getHandlerInternal(ServerWebExchange exchange) {
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
- * @param lookupPath mapping lookup path within the current servlet mapping
+ * @param lookupPath the lookup path within the current mapping
* @param exchange the current exchange
* @return the best-matching handler method, or {@code null} if no match
- * @see #handleMatch(Object, String, ServerWebExchange)
- * @see #handleNoMatch(Set, String, ServerWebExchange)
+ * @see #handleMatch(Object, LookupPath, ServerWebExchange)
+ * @see #handleNoMatch(Set, LookupPath, ServerWebExchange)
*/
@Nullable
- protected HandlerMethod lookupHandlerMethod(String lookupPath, ServerWebExchange exchange)
+ protected HandlerMethod lookupHandlerMethod(LookupPath lookupPath, ServerWebExchange exchange)
throws Exception {
List matches = new ArrayList();
- List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
+ List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath.getPath());
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, exchange);
}
@@ -349,22 +351,22 @@ private void addMatchingMappings(Collection mappings, List matches, Se
/**
* Invoked when a matching mapping is found.
* @param mapping the matching mapping
- * @param lookupPath mapping lookup path within the current servlet mapping
+ * @param lookupPath the lookup path within the current mapping
* @param exchange the current exchange
*/
- protected void handleMatch(T mapping, String lookupPath, ServerWebExchange exchange) {
+ protected void handleMatch(T mapping, LookupPath lookupPath, ServerWebExchange exchange) {
}
/**
* Invoked when no matching mapping is not found.
* @param mappings all registered mappings
- * @param lookupPath mapping lookup path within the current servlet mapping
+ * @param lookupPath the lookup path within the current mapping
* @param exchange the current exchange
* @return an alternative HandlerMethod or {@code null}
* @throws Exception provides details that can be translated into an error status code
*/
@Nullable
- protected HandlerMethod handleNoMatch(Set mappings, String lookupPath, ServerWebExchange exchange)
+ protected HandlerMethod handleNoMatch(Set mappings, LookupPath lookupPath, ServerWebExchange exchange)
throws Exception {
return null;
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfo.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfo.java
index 1b0e5d618869..3b660e6228b3 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfo.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfo.java
@@ -33,7 +33,6 @@
import org.springframework.web.reactive.result.condition.RequestConditionHolder;
import org.springframework.web.reactive.result.condition.RequestMethodsRequestCondition;
import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.support.HttpRequestPathHelper;
/**
* Encapsulates the following request mapping conditions:
@@ -475,9 +474,8 @@ public RequestMappingInfo build() {
RequestedContentTypeResolver contentTypeResolver = this.options.getContentTypeResolver();
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
- this.paths, this.options.getPathHelper(), this.options.getPathMatcher(),
- this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
- this.options.getFileExtensions());
+ this.paths, this.options.getPathMatcher(), this.options.useSuffixPatternMatch(),
+ this.options.useTrailingSlashMatch(), this.options.getFileExtensions());
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(methods),
@@ -498,8 +496,6 @@ public RequestMappingInfo build() {
*/
public static class BuilderConfiguration {
- private HttpRequestPathHelper pathHelper;
-
private PathMatcher pathMatcher;
private boolean trailingSlashMatch = true;
@@ -510,18 +506,6 @@ public static class BuilderConfiguration {
private RequestedContentTypeResolver contentTypeResolver;
- /**
- * Set a custom UrlPathHelper to use for the PatternsRequestCondition.
- * By default this is not set.
- */
- public void setPathHelper(HttpRequestPathHelper pathHelper) {
- this.pathHelper = pathHelper;
- }
-
- public HttpRequestPathHelper getPathHelper() {
- return this.pathHelper;
- }
-
/**
* Set a custom PathMatcher to use for the PatternsRequestCondition.
*
By default this is not set.
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java
index 0e258ccaaa9d..3b6eed42b295 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java
@@ -27,7 +27,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
@@ -35,9 +34,7 @@
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.result.condition.NameValueExpression;
@@ -46,6 +43,8 @@
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
+import org.springframework.web.server.support.LookupPath;
+import org.springframework.web.util.WebUtils;
/**
* Abstract base class for classes for which {@link RequestMappingInfo} defines
@@ -103,7 +102,7 @@ protected Comparator getMappingComparator(final ServerWebExc
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
- protected void handleMatch(RequestMappingInfo info, String lookupPath, ServerWebExchange exchange) {
+ protected void handleMatch(RequestMappingInfo info, LookupPath lookupPath, ServerWebExchange exchange) {
super.handleMatch(info, lookupPath, exchange);
String bestPattern;
@@ -112,13 +111,13 @@ protected void handleMatch(RequestMappingInfo info, String lookupPath, ServerWeb
Set patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
- bestPattern = lookupPath;
+ bestPattern = lookupPath.getPath();
uriVariables = Collections.emptyMap();
decodedUriVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
- uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
+ uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath.getPath());
decodedUriVariables = getPathHelper().decodePathVariables(exchange, uriVariables);
}
@@ -157,43 +156,12 @@ private Map> extractMatrixVariables(
uriVariables.put(uriVar.getKey(), uriVarValue.substring(0, semicolonIndex));
}
- MultiValueMap vars = parseMatrixVariables(matrixVariables);
+ MultiValueMap vars = WebUtils.parseMatrixVariables(matrixVariables);
result.put(uriVar.getKey(), getPathHelper().decodeMatrixVariables(exchange, vars));
}
return result;
}
- /**
- * Parse the given string with matrix variables. An example string would look
- * like this {@code "q1=a;q1=b;q2=a,b,c"}. The resulting map would contain
- * keys {@code "q1"} and {@code "q2"} with values {@code ["a","b"]} and
- * {@code ["a","b","c"]} respectively.
- * @param matrixVariables the unparsed matrix variables string
- * @return a map with matrix variable names and values (never {@code null})
- */
- private static MultiValueMap parseMatrixVariables(String matrixVariables) {
- MultiValueMap result = new LinkedMultiValueMap<>();
- if (!StringUtils.hasText(matrixVariables)) {
- return result;
- }
- StringTokenizer pairs = new StringTokenizer(matrixVariables, ";");
- while (pairs.hasMoreTokens()) {
- String pair = pairs.nextToken();
- int index = pair.indexOf('=');
- if (index != -1) {
- String name = pair.substring(0, index);
- String rawValue = pair.substring(index + 1);
- for (String value : StringUtils.commaDelimitedListToStringArray(rawValue)) {
- result.add(name, value);
- }
- }
- else {
- result.add(pair, "");
- }
- }
- return result;
- }
-
/**
* Iterate all RequestMappingInfos once again, look if any match by URL at
* least and raise exceptions accordingly.
@@ -206,7 +174,7 @@ private static MultiValueMap parseMatrixVariables(String matrixV
* method but not by query parameter conditions
*/
@Override
- protected HandlerMethod handleNoMatch(Set infos, String lookupPath,
+ protected HandlerMethod handleNoMatch(Set infos, LookupPath lookupPath,
ServerWebExchange exchange) throws Exception {
PartialMatchHelper helper = new PartialMatchHelper(infos, exchange);
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
index b3485ec86913..39b61b017513 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java
@@ -37,6 +37,8 @@
import org.springframework.web.reactive.result.condition.RequestCondition;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
+import org.springframework.web.server.support.LookupPath;
+import org.springframework.web.server.ServerWebExchange;
/**
* An extension of {@link RequestMappingInfoHandlerMapping} that creates
@@ -113,10 +115,8 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) {
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
- this.config.setPathHelper(getPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
- this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentTypeResolver(getContentTypeResolver());
@@ -159,7 +159,6 @@ public Set getFileExtensions() {
return this.config.getFileExtensions();
}
-
/**
* {@inheritDoc}
* Expects a handler to have a type-level @{@link Controller} annotation.
@@ -170,6 +169,11 @@ protected boolean isHandler(Class> beanType) {
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
+ @Override
+ protected LookupPath createLookupPath(ServerWebExchange exchange) {
+ return getPathHelper().getLookupPathForRequest(exchange);
+ }
+
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java
index c8b0b87bcf29..82fb55fec055 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java
@@ -50,7 +50,7 @@
import org.springframework.web.reactive.result.HandlerResultHandlerSupport;
import org.springframework.web.server.NotAcceptableStatusException;
import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
/**
* {@code HandlerResultHandler} that encapsulates the view resolution algorithm
@@ -91,8 +91,6 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
private final List defaultViews = new ArrayList<>(4);
- private final HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
-
/**
* Basic constructor with a default {@link ReactiveAdapterRegistry}.
@@ -259,7 +257,7 @@ else if (View.class.isAssignableFrom(clazz)) {
* Use the request path the leading and trailing slash stripped.
*/
private String getDefaultViewName(ServerWebExchange exchange) {
- String path = this.pathHelper.getLookupPathForRequest(exchange);
+ String path = exchange.getAttribute(LookupPath.LOOKUP_PATH_ATTRIBUTE).get().getPath();
if (path.startsWith("/")) {
path = path.substring(1);
}
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java
index df36d448c20e..b92fc911e3e8 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java
@@ -22,6 +22,8 @@
import org.junit.Test;
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
+import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.server.ServerWebExchange;
import static org.junit.Assert.assertEquals;
@@ -80,7 +82,7 @@ public void combineMultiplePatterns() {
@Test
public void matchDirectPath() throws Exception {
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
- PatternsRequestCondition match = condition.getMatchingCondition(get("/foo").toExchange());
+ PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo"));
assertNotNull(match);
}
@@ -88,7 +90,7 @@ public void matchDirectPath() throws Exception {
@Test
public void matchPattern() throws Exception {
PatternsRequestCondition condition = new PatternsRequestCondition("/foo/*");
- PatternsRequestCondition match = condition.getMatchingCondition(get("/foo/bar").toExchange());
+ PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo/bar"));
assertNotNull(match);
}
@@ -96,7 +98,7 @@ public void matchPattern() throws Exception {
@Test
public void matchSortPatterns() throws Exception {
PatternsRequestCondition condition = new PatternsRequestCondition("/*/*", "/foo/bar", "/foo/*");
- PatternsRequestCondition match = condition.getMatchingCondition(get("/foo/bar").toExchange());
+ PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo/bar"));
PatternsRequestCondition expected = new PatternsRequestCondition("/foo/bar", "/foo/*", "/*/*");
assertEquals(expected, match);
@@ -104,7 +106,7 @@ public void matchSortPatterns() throws Exception {
@Test
public void matchSuffixPattern() throws Exception {
- ServerWebExchange exchange = get("/foo.html").toExchange();
+ ServerWebExchange exchange = createExchange("/foo.html");
PatternsRequestCondition condition = new PatternsRequestCondition("/{foo}");
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
@@ -112,7 +114,7 @@ public void matchSuffixPattern() throws Exception {
assertNotNull(match);
assertEquals("/{foo}.*", match.getPatterns().iterator().next());
- condition = new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, false, false, null);
+ condition = new PatternsRequestCondition(new String[] {"/{foo}"}, null,false, false, null);
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
@@ -125,15 +127,15 @@ public void matchSuffixPattern() throws Exception {
public void matchSuffixPatternUsingFileExtensions() throws Exception {
String[] patterns = new String[] {"/jobs/{jobName}"};
Set extensions = Collections.singleton("json");
- PatternsRequestCondition condition = new PatternsRequestCondition(patterns, null, null, true, false, extensions);
+ PatternsRequestCondition condition = new PatternsRequestCondition(patterns, null, true, false, extensions);
- MockServerWebExchange exchange = get("/jobs/my.job").toExchange();
+ MockServerWebExchange exchange = createExchange("/jobs/my.job");
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("/jobs/{jobName}", match.getPatterns().iterator().next());
- exchange = get("/jobs/my.job.json").toExchange();
+ exchange = createExchange("/jobs/my.job.json");
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
@@ -143,14 +145,14 @@ public void matchSuffixPatternUsingFileExtensions() throws Exception {
@Test
public void matchSuffixPatternUsingFileExtensions2() throws Exception {
PatternsRequestCondition condition1 = new PatternsRequestCondition(
- new String[] {"/prefix"}, null, null, true, false, Collections.singleton("json"));
+ new String[] {"/prefix"}, null, true, false, Collections.singleton("json"));
PatternsRequestCondition condition2 = new PatternsRequestCondition(
- new String[] {"/suffix"}, null, null, true, false, null);
+ new String[] {"/suffix"}, null, true, false, null);
PatternsRequestCondition combined = condition1.combine(condition2);
- MockServerWebExchange exchange = get("/prefix/suffix.json").toExchange();
+ MockServerWebExchange exchange = createExchange("/prefix/suffix.json");
PatternsRequestCondition match = combined.getMatchingCondition(exchange);
assertNotNull(match);
@@ -158,7 +160,7 @@ public void matchSuffixPatternUsingFileExtensions2() throws Exception {
@Test
public void matchTrailingSlash() throws Exception {
- MockServerWebExchange exchange = get("/foo/").toExchange();
+ MockServerWebExchange exchange = createExchange("/foo/");
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
PatternsRequestCondition match = condition.getMatchingCondition(exchange);
@@ -166,14 +168,15 @@ public void matchTrailingSlash() throws Exception {
assertNotNull(match);
assertEquals("Should match by default", "/foo/", match.getPatterns().iterator().next());
- condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, true, null);
+ condition = new PatternsRequestCondition(new String[] {"/foo"}, null, false, true, null);
match = condition.getMatchingCondition(exchange);
assertNotNull(match);
assertEquals("Trailing slash should be insensitive to useSuffixPatternMatch settings (SPR-6164, SPR-5636)",
"/foo/", match.getPatterns().iterator().next());
- condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, false, null);
+ exchange = createExchange("/foo/");
+ condition = new PatternsRequestCondition(new String[] {"/foo"}, null, false, false, null);
match = condition.getMatchingCondition(exchange);
assertNull(match);
@@ -182,7 +185,7 @@ public void matchTrailingSlash() throws Exception {
@Test
public void matchPatternContainsExtension() throws Exception {
PatternsRequestCondition condition = new PatternsRequestCondition("/foo.jpg");
- PatternsRequestCondition match = condition.getMatchingCondition(get("/foo.html").toExchange());
+ PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo.html"));
assertNull(match);
}
@@ -192,7 +195,7 @@ public void compareEqualPatterns() throws Exception {
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo*");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo*");
- assertEquals(0, c1.compareTo(c2, get("/foo").toExchange()));
+ assertEquals(0, c1.compareTo(c2, createExchange("/foo")));
}
@Test
@@ -200,12 +203,12 @@ public void comparePatternSpecificity() throws Exception {
PatternsRequestCondition c1 = new PatternsRequestCondition("/fo*");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo");
- assertEquals(1, c1.compareTo(c2, get("/foo").toExchange()));
+ assertEquals(1, c1.compareTo(c2, createExchange("/foo")));
}
@Test
public void compareNumberOfMatchingPatterns() throws Exception {
- ServerWebExchange exchange = get("/foo.html").toExchange();
+ ServerWebExchange exchange = createExchange("/foo.html");
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo", "*.jpeg");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo", "*.html");
@@ -217,5 +220,11 @@ public void compareNumberOfMatchingPatterns() throws Exception {
assertEquals(1, match1.compareTo(match2, exchange));
}
+ private MockServerWebExchange createExchange(String path) {
+ MockServerWebExchange exchange = get(path).toExchange();
+ HttpRequestPathHelper helper = new HttpRequestPathHelper();
+ exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE, helper.getLookupPathForRequest(exchange));
+ return exchange;
+ }
}
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMappingInfoTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMappingInfoTests.java
index 6e9c463a83a6..ac32a58d28a5 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMappingInfoTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/RequestMappingInfoTests.java
@@ -29,6 +29,8 @@
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
+import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import org.springframework.web.server.ServerWebExchange;
import static java.util.Arrays.asList;
@@ -65,6 +67,7 @@ public void createEmpty() {
@Test
public void matchPatternsCondition() {
MockServerWebExchange exchange = MockServerHttpRequest.get("/foo").toExchange();
+ setLookupPathAttribute(exchange);
RequestMappingInfo info = paths("/foo*", "/bar").build();
RequestMappingInfo expected = paths("/foo*").build();
@@ -80,6 +83,7 @@ public void matchPatternsCondition() {
@Test
public void matchParamsCondition() {
ServerWebExchange exchange = MockServerHttpRequest.get("/foo?foo=bar").toExchange();
+ setLookupPathAttribute(exchange);
RequestMappingInfo info = paths("/foo").params("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(exchange);
@@ -95,6 +99,7 @@ public void matchParamsCondition() {
@Test
public void matchHeadersCondition() {
ServerWebExchange exchange = MockServerHttpRequest.get("/foo").header("foo", "bar").toExchange();
+ setLookupPathAttribute(exchange);
RequestMappingInfo info = paths("/foo").headers("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(exchange);
@@ -110,6 +115,7 @@ public void matchHeadersCondition() {
@Test
public void matchConsumesCondition() {
ServerWebExchange exchange = MockServerHttpRequest.post("/foo").contentType(MediaType.TEXT_PLAIN).toExchange();
+ setLookupPathAttribute(exchange);
RequestMappingInfo info = paths("/foo").consumes("text/plain").build();
RequestMappingInfo match = info.getMatchingCondition(exchange);
@@ -125,6 +131,7 @@ public void matchConsumesCondition() {
@Test
public void matchProducesCondition() {
ServerWebExchange exchange = MockServerHttpRequest.get("/foo").accept(MediaType.TEXT_PLAIN).toExchange();
+ setLookupPathAttribute(exchange);
RequestMappingInfo info = paths("/foo").produces("text/plain").build();
RequestMappingInfo match = info.getMatchingCondition(exchange);
@@ -140,7 +147,8 @@ public void matchProducesCondition() {
@Test
public void matchCustomCondition() {
ServerWebExchange exchange = MockServerHttpRequest.get("/foo?foo=bar").toExchange();
-
+ setLookupPathAttribute(exchange);
+
RequestMappingInfo info = paths("/foo").params("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(exchange);
@@ -161,6 +169,7 @@ public void compareTwoHttpMethodsOneParam() {
RequestMappingInfo oneMethodOneParam = paths().methods(RequestMethod.GET).params("foo").build();
ServerWebExchange exchange = MockServerHttpRequest.get("/foo").toExchange();
+ setLookupPathAttribute(exchange);
Comparator comparator = (info, otherInfo) -> info.compareTo(otherInfo, exchange);
List list = asList(none, oneMethod, oneMethodOneParam);
@@ -270,4 +279,9 @@ public void preFlightRequest() throws Exception {
assertNull("Pre-flight should match the ACCESS_CONTROL_REQUEST_METHOD", match);
}
+ public void setLookupPathAttribute(ServerWebExchange exchange) {
+ HttpRequestPathHelper helper = new HttpRequestPathHelper();
+ exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE, helper.getLookupPathForRequest(exchange));
+ }
+
}
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java
index a48cb5555552..0def9b7e1eff 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java
@@ -54,6 +54,7 @@
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
@@ -72,11 +73,15 @@ public class RequestMappingInfoHandlerMappingTests {
private TestRequestMappingInfoHandlerMapping handlerMapping;
+ private HttpRequestPathHelper pathHelper;
+
@Before
public void setup() throws Exception {
this.handlerMapping = new TestRequestMappingInfoHandlerMapping();
this.handlerMapping.registerHandler(new TestController());
+ this.pathHelper = new HttpRequestPathHelper();
+ this.handlerMapping.setPathHelper(this.pathHelper);
}
@@ -208,8 +213,8 @@ public void getHandlerProducibleMediaTypesAttribute() throws Exception {
@Test
@SuppressWarnings("unchecked")
public void handleMatchUriTemplateVariables() throws Exception {
- String lookupPath = "/1/2";
- ServerWebExchange exchange = get(lookupPath).toExchange();
+ ServerWebExchange exchange = get("/1/2").toExchange();
+ LookupPath lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
RequestMappingInfo key = paths("/{path1}/{path2}").build();
this.handlerMapping.handleMatch(key, lookupPath, exchange);
@@ -228,11 +233,10 @@ public void handleMatchUriTemplateVariablesDecode() throws Exception {
URI url = URI.create("/group/a%2Fb");
ServerWebExchange exchange = MockServerHttpRequest.method(HttpMethod.GET, url).toExchange();
- HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
- pathHelper.setUrlDecode(false);
- String lookupPath = pathHelper.getLookupPathForRequest(exchange);
-
- this.handlerMapping.setPathHelper(pathHelper);
+ this.pathHelper.setUrlDecode(false);
+ LookupPath lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
+ this.handlerMapping.setPathHelper(this.pathHelper);
+
this.handlerMapping.handleMatch(key, lookupPath, exchange);
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
@@ -248,7 +252,8 @@ public void handleMatchUriTemplateVariablesDecode() throws Exception {
public void handleMatchBestMatchingPatternAttribute() throws Exception {
RequestMappingInfo key = paths("/{path1}/2", "/**").build();
ServerWebExchange exchange = get("/1/2").toExchange();
- this.handlerMapping.handleMatch(key, "/1/2", exchange);
+ LookupPath lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
+ this.handlerMapping.handleMatch(key, lookupPath, exchange);
assertEquals("/{path1}/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE));
}
@@ -257,8 +262,8 @@ public void handleMatchBestMatchingPatternAttribute() throws Exception {
public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() throws Exception {
RequestMappingInfo key = paths().build();
ServerWebExchange exchange = get("/1/2").toExchange();
-
- this.handlerMapping.handleMatch(key, "/1/2", exchange);
+ LookupPath lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
+ this.handlerMapping.handleMatch(key, lookupPath, exchange);
assertEquals("/1/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE));
}
@@ -268,8 +273,8 @@ public void handleMatchMatrixVariables() throws Exception {
MultiValueMap matrixVariables;
Map uriVariables;
- ServerWebExchange exchange = get("/").toExchange();
- handleMatch(exchange, "/{cars}", "/cars;colors=red,blue,green;year=2012");
+ ServerWebExchange exchange = get("/cars;colors=red,blue,green;year=2012").toExchange();
+ handleMatch(exchange, "/{cars}");
matrixVariables = getMatrixVariables(exchange, "cars");
uriVariables = getUriTemplateVariables(exchange);
@@ -279,8 +284,8 @@ public void handleMatchMatrixVariables() throws Exception {
assertEquals("2012", matrixVariables.getFirst("year"));
assertEquals("cars", uriVariables.get("cars"));
- exchange = get("/").toExchange();
- handleMatch(exchange, "/{cars:[^;]+}{params}", "/cars;colors=red,blue,green;year=2012");
+ exchange = get("/cars;colors=red,blue,green;year=2012").toExchange();
+ handleMatch(exchange, "/{cars:[^;]+}{params}");
matrixVariables = getMatrixVariables(exchange, "params");
uriVariables = getUriTemplateVariables(exchange);
@@ -291,8 +296,8 @@ public void handleMatchMatrixVariables() throws Exception {
assertEquals("cars", uriVariables.get("cars"));
assertEquals(";colors=red,blue,green;year=2012", uriVariables.get("params"));
- exchange = get("/").toExchange();
- handleMatch(exchange, "/{cars:[^;]+}{params}", "/cars");
+ exchange = get("/cars").toExchange();
+ handleMatch(exchange, "/{cars:[^;]+}{params}");
matrixVariables = getMatrixVariables(exchange, "params");
uriVariables = getUriTemplateVariables(exchange);
@@ -308,8 +313,8 @@ public void handleMatchMatrixVariablesDecoding() throws Exception {
urlPathHelper.setUrlDecode(false);
this.handlerMapping.setPathHelper(urlPathHelper);
- ServerWebExchange exchange = get("/").toExchange();
- handleMatch(exchange, "/path{filter}", "/path;mvar=a%2fb");
+ ServerWebExchange exchange = get("/path;mvar=a%2fb").toExchange();
+ handleMatch(exchange, "/path{filter}");
MultiValueMap matrixVariables = getMatrixVariables(exchange, "filter");
Map uriVariables = getUriTemplateVariables(exchange);
@@ -368,8 +373,9 @@ private void testMediaTypeNotAcceptable(String url) throws Exception {
ex.getSupportedMediaTypes()));
}
- private void handleMatch(ServerWebExchange exchange, String pattern, String lookupPath) {
+ private void handleMatch(ServerWebExchange exchange, String pattern) {
RequestMappingInfo info = paths(pattern).build();
+ LookupPath lookupPath = this.pathHelper.getLookupPathForRequest(exchange);
this.handlerMapping.handleMatch(info, lookupPath, exchange);
}
@@ -474,7 +480,6 @@ protected RequestMappingInfo getMappingForMethod(Method method, Class> handler
RequestMapping annot = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (annot != null) {
BuilderConfiguration options = new BuilderConfiguration();
- options.setPathHelper(getPathHelper());
options.setPathMatcher(getPathMatcher());
options.setSuffixPatternMatch(true);
options.setTrailingSlashMatch(true);
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java
index 7d11620c3eb7..3fcf341e8274 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java
@@ -55,6 +55,8 @@
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.server.NotAcceptableStatusException;
import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.support.HttpRequestPathHelper;
+import org.springframework.web.server.support.LookupPath;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
@@ -217,14 +219,17 @@ private void testDefaultViewName(Object returnValue, MethodParameter returnType)
ViewResolutionResultHandler handler = resultHandler(new TestViewResolver("account"));
MockServerWebExchange exchange = get("/account").toExchange();
+ addLookupPathAttribute(exchange);
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = get("/account/").toExchange();
+ addLookupPathAttribute(exchange);
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
exchange = get("/account.123").toExchange();
+ addLookupPathAttribute(exchange);
handler.handleResult(exchange, result).block(Duration.ofMillis(5000));
assertResponseBody(exchange, "account: {id=123}");
}
@@ -251,7 +256,8 @@ public void contentNegotiation() throws Exception {
HandlerResult handlerResult = new HandlerResult(new Object(), value, returnType, this.bindingContext);
MockServerWebExchange exchange = get("/account").accept(APPLICATION_JSON).toExchange();
-
+ addLookupPathAttribute(exchange);
+
TestView defaultView = new TestView("jsonView", APPLICATION_JSON);
resultHandler(Collections.singletonList(defaultView), new TestViewResolver("account"))
@@ -301,6 +307,11 @@ public void contentNegotiationWithRedirect() throws Exception {
assertEquals("/", response.getHeaders().getLocation().toString());
}
+ private void addLookupPathAttribute(ServerWebExchange exchange) {
+ HttpRequestPathHelper helper = new HttpRequestPathHelper();
+ exchange.getAttributes().put(LookupPath.LOOKUP_PATH_ATTRIBUTE, helper.getLookupPathForRequest(exchange));
+ }
+
private ViewResolutionResultHandler resultHandler(ViewResolver... resolvers) {
return resultHandler(Collections.emptyList(), resolvers);
@@ -322,6 +333,7 @@ private ServerWebExchange testHandle(String path, MethodParameter returnType, Ob
model.addAttribute("id", "123");
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
MockServerWebExchange exchange = get(path).toExchange();
+ addLookupPathAttribute(exchange);
resultHandler(resolvers).handleResult(exchange, result).block(Duration.ofSeconds(5));
assertResponseBody(exchange, responseBody);
return exchange;