Browse Source

WebFlux compiles after PathPattern/Container changes

pull/1735/head
Rossen Stoyanchev 8 years ago
parent
commit
8581afa621
  1. 20
      spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java
  2. 139
      spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java
  3. 190
      spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternMatcherTests.java
  4. 20
      spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java
  5. 4
      spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java
  6. 8
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java
  7. 80
      spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java
  8. 2
      spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java
  9. 26
      spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java

20
spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java

@ -19,10 +19,10 @@ package org.springframework.web.util.pattern; @@ -19,10 +19,10 @@ package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import org.springframework.http.server.reactive.PathContainer;
import org.springframework.lang.Nullable;
@ -79,19 +79,17 @@ public class ParsingPathMatcher implements PathMatcher { @@ -79,19 +79,17 @@ public class ParsingPathMatcher implements PathMatcher {
@Override
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
PathPattern pathPattern = getPathPattern(pattern);
Map<String, PathMatchResult> results = pathPattern.matchAndExtract(PathContainer.parse(path, StandardCharsets.UTF_8));
// Collapse PathMatchResults to simple value results (path parameters are lost in this translation)
Map<String, String> boundVariables = null;
if (results.size() == 0) {
boundVariables = Collections.emptyMap();
PathContainer pathContainer = PathContainer.parse(path, StandardCharsets.UTF_8);
PathMatchResult results = pathPattern.matchAndExtract(pathContainer);
// Collapse PathMatchResults to simple value results
// TODO: (path parameters are lost in this translation)
if (results.getUriVariables().size() == 0) {
return Collections.emptyMap();
}
else {
boundVariables = new LinkedHashMap<>();
for (Map.Entry<String,PathMatchResult> entries: results.entrySet()) {
boundVariables.put(entries.getKey(), entries.getValue().value());
}
return results.getUriVariables().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
return boundVariables;
}
@Override

139
spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java

@ -27,6 +27,7 @@ import org.springframework.http.server.reactive.PathContainer.Element; @@ -27,6 +27,7 @@ import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment;
import org.springframework.http.server.reactive.PathContainer.Separator;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
@ -155,6 +156,26 @@ public class PathPattern implements Comparable<PathPattern> { @@ -155,6 +156,26 @@ public class PathPattern implements Comparable<PathPattern> {
return this.patternString;
}
// TODO: remove String-variants
public boolean matches(String path) {
return matches(PathContainer.parse(path, StandardCharsets.UTF_8));
}
public PathMatchResult matchAndExtract(String path) {
return matchAndExtract(PathContainer.parse(path, StandardCharsets.UTF_8));
}
@Nullable
public PathRemainingMatchInfo getPathRemaining(@Nullable String path) {
return getPathRemaining(path != null ?
PathContainer.parse(path, StandardCharsets.UTF_8) : null);
}
/**
* @param pathContainer the candidate path container to attempt to match against this pattern
* @return true if the path matches this pattern
@ -199,11 +220,11 @@ public class PathPattern implements Comparable<PathPattern> { @@ -199,11 +220,11 @@ public class PathPattern implements Comparable<PathPattern> {
else {
PathRemainingMatchInfo info;
if (matchingContext.remainingPathIndex == pathContainer.elements().size()) {
info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getExtractedVariables());
info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getPathMatchResult());
}
else {
info = new PathRemainingMatchInfo(PathContainer.subPath(pathContainer, matchingContext.remainingPathIndex),
matchingContext.getExtractedVariables());
matchingContext.getPathMatchResult());
}
return info;
}
@ -230,13 +251,13 @@ public class PathPattern implements Comparable<PathPattern> { @@ -230,13 +251,13 @@ public class PathPattern implements Comparable<PathPattern> {
* @return a map of extracted variables - an empty map if no variables extracted.
* @throws IllegalStateException if the path does not match the pattern
*/
public Map<String, PathMatchResult> matchAndExtract(PathContainer pathContainer) {
public PathMatchResult matchAndExtract(PathContainer pathContainer) {
MatchingContext matchingContext = new MatchingContext(pathContainer, true);
if (this.head != null && this.head.matches(0, matchingContext)) {
return matchingContext.getExtractedVariables();
return matchingContext.getPathMatchResult();
}
else if (!hasLength(pathContainer)) {
return Collections.emptyMap();
return PathMatchResult.EMPTY;
}
else {
throw new IllegalStateException("Pattern \"" + this + "\" is not a match for \"" + pathContainer.value() + "\"");
@ -447,45 +468,46 @@ public class PathPattern implements Comparable<PathPattern> { @@ -447,45 +468,46 @@ public class PathPattern implements Comparable<PathPattern> {
return this.patternString;
}
/**
* Represents the result of a successful variable match. This holds the key that matched, the
* value that was found for that key and, if any, the parameters attached to that path element.
* For example: "/{var}" against "/foo;a=b" will return a PathMathResult with 'key=var',
* 'value=foo' and parameters 'a=b'.
* Represents the result of a successful path match. This holds the keys that matched, the
* values that were found for each key and, if any, the path parameters (matrix variables)
* attached to that path element.
* For example: "/{var}" against "/foo;a=b" will return a PathMathResult with 'foo=bar'
* for URI variables and 'a=b' as path parameters for 'foo'.
*/
public static class PathMatchResult {
private final String key;
private final String value;
private final MultiValueMap<String,String> parameters;
public PathMatchResult(String key, String value, MultiValueMap<String, String> parameters) {
this.key = key;
this.value = value;
this.parameters = parameters;
private static final PathMatchResult EMPTY =
new PathMatchResult(Collections.emptyMap(), Collections.emptyMap());
private final Map<String, String> uriVariables;
private final Map<String, MultiValueMap<String, String>> matrixVariables;
public PathMatchResult(Map<String, String> uriVars,
@Nullable Map<String, MultiValueMap<String, String>> matrixVars) {
this.uriVariables = Collections.unmodifiableMap(uriVars);
this.matrixVariables = matrixVars != null ?
Collections.unmodifiableMap(matrixVars) : Collections.emptyMap();
}
/**
* @return match result key
*/
public String key() {
return key;
public Map<String, String> getUriVariables() {
return this.uriVariables;
}
/**
* @return match result value
*/
public String value() {
return this.value;
public Map<String, MultiValueMap<String, String>> getMatrixVariables() {
return this.matrixVariables;
}
/**
* @return match result parameters (empty map if no parameters)
*/
public MultiValueMap<String,String> parameters() {
return this.parameters;
@Override
public String toString() {
return "PathMatchResult[uriVariables=" + this.uriVariables + ", " +
"matrixVariables=" + this.matrixVariables + "]";
}
}
@ -498,15 +520,15 @@ public class PathPattern implements Comparable<PathPattern> { @@ -498,15 +520,15 @@ public class PathPattern implements Comparable<PathPattern> {
private final PathContainer pathRemaining;
private final Map<String, PathMatchResult> matchingVariables;
private final PathMatchResult pathMatchResult;
PathRemainingMatchInfo(@Nullable PathContainer pathRemaining) {
this(pathRemaining, Collections.emptyMap());
this(pathRemaining, PathMatchResult.EMPTY);
}
PathRemainingMatchInfo(@Nullable PathContainer pathRemaining, Map<String, PathMatchResult> matchingVariables) {
PathRemainingMatchInfo(@Nullable PathContainer pathRemaining, PathMatchResult pathMatchResult) {
this.pathRemaining = pathRemaining;
this.matchingVariables = matchingVariables;
this.pathMatchResult = pathMatchResult;
}
/**
@ -520,8 +542,15 @@ public class PathPattern implements Comparable<PathPattern> { @@ -520,8 +542,15 @@ public class PathPattern implements Comparable<PathPattern> {
* Return variables that were bound in the part of the path that was successfully matched.
* Will be an empty map if no variables were bound
*/
public Map<String, PathMatchResult> getMatchingVariables() {
return this.matchingVariables;
public Map<String, String> getUriVariables() {
return this.pathMatchResult.getUriVariables();
}
/**
* Return the path parameters for each bound variable.
*/
public Map<String, MultiValueMap<String, String>> getMatrixVariables() {
return this.pathMatchResult.getMatrixVariables();
}
}
@ -595,7 +624,10 @@ public class PathPattern implements Comparable<PathPattern> { @@ -595,7 +624,10 @@ public class PathPattern implements Comparable<PathPattern> {
boolean isMatchStartMatching = false;
@Nullable
private Map<String, PathMatchResult> extractedVariables;
private Map<String, String> extractedUriVariables;
@Nullable
private Map<String, MultiValueMap<String, String>> extractedMatrixVariables;
boolean extractingVariables;
@ -626,18 +658,25 @@ public class PathPattern implements Comparable<PathPattern> { @@ -626,18 +658,25 @@ public class PathPattern implements Comparable<PathPattern> {
}
public void set(String key, String value, MultiValueMap<String,String> parameters) {
if (this.extractedVariables == null) {
extractedVariables = new HashMap<>();
if (this.extractedUriVariables == null) {
this.extractedUriVariables = new HashMap<>();
}
this.extractedUriVariables.put(key, value);
if (!parameters.isEmpty()) {
if (this.extractedMatrixVariables == null) {
this.extractedMatrixVariables = new HashMap<>();
}
this.extractedMatrixVariables.put(key, CollectionUtils.unmodifiableMultiValueMap(parameters));
}
extractedVariables.put(key, new PathMatchResult(key, value, parameters));
}
public Map<String, PathMatchResult> getExtractedVariables() {
if (this.extractedVariables == null) {
return Collections.emptyMap();
public PathMatchResult getPathMatchResult() {
if (this.extractedUriVariables == null) {
return PathMatchResult.EMPTY;
}
else {
return this.extractedVariables;
return new PathMatchResult(this.extractedUriVariables, this.extractedMatrixVariables);
}
}

190
spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternMatcherTests.java

@ -29,17 +29,20 @@ import org.junit.Ignore; @@ -29,17 +29,20 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.http.server.reactive.PathContainer;
import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.util.pattern.ParsingPathMatcher;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPattern.PathMatchResult;
import org.springframework.web.util.pattern.PathPattern.PathRemainingMatchInfo;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Exercise matching of {@link PathPattern} objects.
@ -125,26 +128,26 @@ public class PathPatternMatcherTests { @@ -125,26 +128,26 @@ public class PathPatternMatcherTests {
// CaptureVariablePathElement
pp = parse("/{var}");
assertMatches(pp,"/resource");
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource")).get("var").value());
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource")).getUriVariables().get("var"));
assertMatches(pp,"/resource/");
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).get("var").value());
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).getUriVariables().get("var"));
assertNoMatch(pp,"/resource//");
pp = parse("/{var}/");
assertNoMatch(pp,"/resource");
assertMatches(pp,"/resource/");
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).get("var").value());
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).getUriVariables().get("var"));
assertNoMatch(pp,"/resource//");
// CaptureTheRestPathElement
pp = parse("/{*var}");
assertMatches(pp,"/resource");
assertEquals("/resource",pp.matchAndExtract(toPathContainer("/resource")).get("var").value());
assertEquals("/resource",pp.matchAndExtract(toPathContainer("/resource")).getUriVariables().get("var"));
assertMatches(pp,"/resource/");
assertEquals("/resource/",pp.matchAndExtract(toPathContainer("/resource/")).get("var").value());
assertEquals("/resource/",pp.matchAndExtract(toPathContainer("/resource/")).getUriVariables().get("var"));
assertMatches(pp,"/resource//");
assertEquals("/resource//",pp.matchAndExtract(toPathContainer("/resource//")).get("var").value());
assertEquals("/resource//",pp.matchAndExtract(toPathContainer("/resource//")).getUriVariables().get("var"));
assertMatches(pp,"//resource//");
assertEquals("//resource//",pp.matchAndExtract(toPathContainer("//resource//")).get("var").value());
assertEquals("//resource//",pp.matchAndExtract(toPathContainer("//resource//")).getUriVariables().get("var"));
// WildcardTheRestPathElement
pp = parse("/**");
@ -166,17 +169,17 @@ public class PathPatternMatcherTests { @@ -166,17 +169,17 @@ public class PathPatternMatcherTests {
// RegexPathElement
pp = parse("/{var1}_{var2}");
assertMatches(pp,"/res1_res2");
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2")).get("var1").value());
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2")).get("var2").value());
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2")).getUriVariables().get("var1"));
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2")).getUriVariables().get("var2"));
assertMatches(pp,"/res1_res2/");
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var1").value());
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var2").value());
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var1"));
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var2"));
assertNoMatch(pp,"/res1_res2//");
pp = parse("/{var1}_{var2}/");
assertNoMatch(pp,"/res1_res2");
assertMatches(pp,"/res1_res2/");
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var1").value());
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var2").value());
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var1"));
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var2"));
assertNoMatch(pp,"/res1_res2//");
pp = parse("/{var1}*");
assertMatches(pp,"/a");
@ -210,25 +213,25 @@ public class PathPatternMatcherTests { @@ -210,25 +213,25 @@ public class PathPatternMatcherTests {
// CaptureVariablePathElement
pp = parser.parse("/{var}");
assertMatches(pp,"/resource");
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource")).get("var").value());
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource")).getUriVariables().get("var"));
assertNoMatch(pp,"/resource/");
assertNoMatch(pp,"/resource//");
pp = parser.parse("/{var}/");
assertNoMatch(pp,"/resource");
assertMatches(pp,"/resource/");
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).get("var").value());
assertEquals("resource",pp.matchAndExtract(toPathContainer("/resource/")).getUriVariables().get("var"));
assertNoMatch(pp,"/resource//");
// CaptureTheRestPathElement
pp = parser.parse("/{*var}");
assertMatches(pp,"/resource");
assertEquals("/resource",pp.matchAndExtract(toPathContainer("/resource")).get("var").value());
assertEquals("/resource",pp.matchAndExtract(toPathContainer("/resource")).getUriVariables().get("var"));
assertMatches(pp,"/resource/");
assertEquals("/resource/",pp.matchAndExtract(toPathContainer("/resource/")).get("var").value());
assertEquals("/resource/",pp.matchAndExtract(toPathContainer("/resource/")).getUriVariables().get("var"));
assertMatches(pp,"/resource//");
assertEquals("/resource//",pp.matchAndExtract(toPathContainer("/resource//")).get("var").value());
assertEquals("/resource//",pp.matchAndExtract(toPathContainer("/resource//")).getUriVariables().get("var"));
assertMatches(pp,"//resource//");
assertEquals("//resource//",pp.matchAndExtract(toPathContainer("//resource//")).get("var").value());
assertEquals("//resource//",pp.matchAndExtract(toPathContainer("//resource//")).getUriVariables().get("var"));
// WildcardTheRestPathElement
pp = parser.parse("/**");
@ -250,15 +253,15 @@ public class PathPatternMatcherTests { @@ -250,15 +253,15 @@ public class PathPatternMatcherTests {
// RegexPathElement
pp = parser.parse("/{var1}_{var2}");
assertMatches(pp,"/res1_res2");
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2")).get("var1").value());
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2")).get("var2").value());
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2")).getUriVariables().get("var1"));
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2")).getUriVariables().get("var2"));
assertNoMatch(pp,"/res1_res2/");
assertNoMatch(pp,"/res1_res2//");
pp = parser.parse("/{var1}_{var2}/");
assertNoMatch(pp,"/res1_res2");
assertMatches(pp,"/res1_res2/");
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var1").value());
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).get("var2").value());
assertEquals("res1",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var1"));
assertEquals("res2",pp.matchAndExtract(toPathContainer("/res1_res2/")).getUriVariables().get("var2"));
assertNoMatch(pp,"/res1_res2//");
pp = parser.parse("/{var1}*");
assertMatches(pp,"/a");
@ -337,23 +340,23 @@ public class PathPatternMatcherTests { @@ -337,23 +340,23 @@ public class PathPatternMatcherTests {
PathPattern.PathRemainingMatchInfo pri = parse("/aaa/{bbb}/c?d/e*f/*/g").getPathRemaining(toPathContainer("/aaa/b/ccd/ef/x/g/i"));
assertEquals("/i",pri.getPathRemaining());
assertEquals("b",pri.getMatchingVariables().get("bbb").value());
assertEquals("b",pri.getUriVariables().get("bbb"));
pri = parse("/aaa/{bbb}/c?d/e*f/*/g/").getPathRemaining(toPathContainer("/aaa/b/ccd/ef/x/g/i"));
assertEquals("i",pri.getPathRemaining());
assertEquals("b",pri.getMatchingVariables().get("bbb").value());
assertEquals("b",pri.getUriVariables().get("bbb"));
pri = parse("/{aaa}_{bbb}/e*f/{x}/g").getPathRemaining(toPathContainer("/aa_bb/ef/x/g/i"));
assertEquals("/i",pri.getPathRemaining());
assertEquals("aa",pri.getMatchingVariables().get("aaa").value());
assertEquals("bb",pri.getMatchingVariables().get("bbb").value());
assertEquals("x",pri.getMatchingVariables().get("x").value());
assertEquals("aa",pri.getUriVariables().get("aaa"));
assertEquals("bb",pri.getUriVariables().get("bbb"));
assertEquals("x",pri.getUriVariables().get("x"));
assertNull(parse("/a/b").getPathRemaining(toPathContainer("")));
assertNull(parse("/a/b").getPathRemaining(null));
assertNull(parse("/a/b").getPathRemaining((String) null));
assertEquals("/a/b",parse("").getPathRemaining(toPathContainer("/a/b")).getPathRemaining());
assertEquals("",parse("").getPathRemaining(toPathContainer("")).getPathRemaining());
assertNull(parse("").getPathRemaining(null).getPathRemaining());
assertNull(parse("").getPathRemaining((String) null).getPathRemaining());
}
@Test
@ -550,30 +553,30 @@ public class PathPatternMatcherTests { @@ -550,30 +553,30 @@ public class PathPatternMatcherTests {
pp = parse("/{this}/{one}/{here}");
pri = getPathRemaining(pp, "/foo/bar/goo/boo");
assertEquals("/boo",pri.getPathRemaining());
assertEquals("foo",pri.getMatchingVariables().get("this").value());
assertEquals("bar",pri.getMatchingVariables().get("one").value());
assertEquals("goo",pri.getMatchingVariables().get("here").value());
assertEquals("foo",pri.getUriVariables().get("this"));
assertEquals("bar",pri.getUriVariables().get("one"));
assertEquals("goo",pri.getUriVariables().get("here"));
pp = parse("/aaa/{foo}");
pri = getPathRemaining(pp, "/aaa/bbb");
assertEquals("",pri.getPathRemaining());
assertEquals("bbb",pri.getMatchingVariables().get("foo").value());
assertEquals("bbb",pri.getUriVariables().get("foo"));
pp = parse("/aaa/bbb");
pri = getPathRemaining(pp, "/aaa/bbb");
assertEquals("",pri.getPathRemaining());
assertEquals(0,pri.getMatchingVariables().size());
assertEquals(0,pri.getUriVariables().size());
pp = parse("/*/{foo}/b*");
pri = getPathRemaining(pp, "/foo");
assertNull(pri);
pri = getPathRemaining(pp, "/abc/def/bhi");
assertEquals("",pri.getPathRemaining());
assertEquals("def",pri.getMatchingVariables().get("foo").value());
assertEquals("def",pri.getUriVariables().get("foo"));
pri = getPathRemaining(pp, "/abc/def/bhi/jkl");
assertEquals("/jkl",pri.getPathRemaining());
assertEquals("def",pri.getMatchingVariables().get("foo").value());
assertEquals("def",pri.getUriVariables().get("foo"));
}
@Test
@ -953,7 +956,7 @@ public class PathPatternMatcherTests { @@ -953,7 +956,7 @@ public class PathPatternMatcherTests {
catch (IllegalStateException e) {
assertEquals("Pattern \"\" is not a match for \"/abc\"", e.getMessage());
}
assertEquals(0, checkCapture("", "").size());
assertEquals(0, checkCapture("", "").getUriVariables().size());
checkCapture("{id}", "99", "id", "99");
checkCapture("/customer/{customerId}", "/customer/78", "customerId", "78");
checkCapture("/customer/{customerId}/banana", "/customer/42/banana", "customerId",
@ -962,8 +965,8 @@ public class PathPatternMatcherTests { @@ -962,8 +965,8 @@ public class PathPatternMatcherTests {
checkCapture("/foo/{bar}/boo/{baz}", "/foo/plum/boo/apple", "bar", "plum", "baz",
"apple");
checkCapture("/{bla}.*", "/testing.html", "bla", "testing");
Map<String, PathMatchResult> extracted = checkCapture("/abc", "/abc");
assertEquals(0, extracted.size());
PathMatchResult extracted = checkCapture("/abc", "/abc");
assertEquals(0, extracted.getUriVariables().size());
checkCapture("/{bla}/foo","/a/foo");
}
@ -973,14 +976,14 @@ public class PathPatternMatcherTests { @@ -973,14 +976,14 @@ public class PathPatternMatcherTests {
PathPattern p = null;
p = pp.parse("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar");
Map<String, PathMatchResult> result = matchAndExtract(p, "com.example-1.0.0.jar");
assertEquals("com.example", result.get("symbolicName").value());
assertEquals("1.0.0", result.get("version").value());
PathMatchResult result = matchAndExtract(p, "com.example-1.0.0.jar");
assertEquals("com.example", result.getUriVariables().get("symbolicName"));
assertEquals("1.0.0", result.getUriVariables().get("version"));
p = pp.parse("{symbolicName:[\\w\\.]+}-sources-{version:[\\w\\.]+}.jar");
result = matchAndExtract(p, "com.example-sources-1.0.0.jar");
assertEquals("com.example", result.get("symbolicName").value());
assertEquals("1.0.0", result.get("version").value());
assertEquals("com.example", result.getUriVariables().get("symbolicName"));
assertEquals("1.0.0", result.getUriVariables().get("version"));
}
@Test
@ -988,22 +991,22 @@ public class PathPatternMatcherTests { @@ -988,22 +991,22 @@ public class PathPatternMatcherTests {
PathPatternParser pp = new PathPatternParser();
PathPattern p = pp.parse("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar");
Map<String, PathMatchResult> result = p.matchAndExtract(toPathContainer("com.example-sources-1.0.0.jar"));
assertEquals("com.example", result.get("symbolicName").value());
assertEquals("1.0.0", result.get("version").value());
PathMatchResult result = p.matchAndExtract(toPathContainer("com.example-sources-1.0.0.jar"));
assertEquals("com.example", result.getUriVariables().get("symbolicName"));
assertEquals("1.0.0", result.getUriVariables().get("version"));
p = pp.parse("{symbolicName:[\\w\\.]+}-sources-{version:[\\d\\.]+}-{year:\\d{4}}{month:\\d{2}}{day:\\d{2}}.jar");
result = matchAndExtract(p,"com.example-sources-1.0.0-20100220.jar");
assertEquals("com.example", result.get("symbolicName").value());
assertEquals("1.0.0", result.get("version").value());
assertEquals("2010", result.get("year").value());
assertEquals("02", result.get("month").value());
assertEquals("20", result.get("day").value());
assertEquals("com.example", result.getUriVariables().get("symbolicName"));
assertEquals("1.0.0", result.getUriVariables().get("version"));
assertEquals("2010", result.getUriVariables().get("year"));
assertEquals("02", result.getUriVariables().get("month"));
assertEquals("20", result.getUriVariables().get("day"));
p = pp.parse("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.\\{\\}]+}.jar");
result = matchAndExtract(p, "com.example-sources-1.0.0.{12}.jar");
assertEquals("com.example", result.get("symbolicName").value());
assertEquals("1.0.0.{12}", result.get("version").value());
assertEquals("com.example", result.getUriVariables().get("symbolicName"));
assertEquals("1.0.0.{12}", result.getUriVariables().get("version"));
}
@Test
@ -1137,12 +1140,12 @@ public class PathPatternMatcherTests { @@ -1137,12 +1140,12 @@ public class PathPatternMatcherTests {
PathPatternParser parser = new PathPatternParser();
PathPattern p1 = parser.parse("/{foo}");
PathPattern p2 = parser.parse("/{foo}.*");
Map<String, PathMatchResult> r1 = matchAndExtract(p1, "/file.txt");
Map<String, PathMatchResult> r2 = matchAndExtract(p2, "/file.txt");
PathMatchResult r1 = matchAndExtract(p1, "/file.txt");
PathMatchResult r2 = matchAndExtract(p2, "/file.txt");
// works fine
assertEquals("file.txt", r1.get("foo").value());
assertEquals("file", r2.get("foo").value());
assertEquals("file.txt", r1.getUriVariables().get("foo"));
assertEquals("file", r2.getUriVariables().get("foo"));
// This produces 2 (see comments in https://jira.spring.io/browse/SPR-14544 )
// Comparator<String> patternComparator = new AntPathMatcher().getPatternComparator("");
@ -1269,38 +1272,38 @@ public class PathPatternMatcherTests { @@ -1269,38 +1272,38 @@ public class PathPatternMatcherTests {
@Test
public void parameters() {
// CaptureVariablePathElement
Map<String, PathMatchResult> result = matchAndExtract("/abc/{var}","/abc/one;two=three;four=five");
assertEquals("one",result.get("var").value());
assertEquals("[three]",result.get("var").parameters().get("two").toString());
assertEquals("[five]",result.get("var").parameters().get("four").toString());
PathMatchResult result = matchAndExtract("/abc/{var}","/abc/one;two=three;four=five");
assertEquals("one",result.getUriVariables().get("var"));
assertEquals("three",result.getMatrixVariables().get("var").getFirst("two"));
assertEquals("five",result.getMatrixVariables().get("var").getFirst("four"));
// RegexPathElement
result = matchAndExtract("/abc/{var1}_{var2}","/abc/123_456;a=b;c=d");
assertEquals("123",result.get("var1").value());
assertEquals("456",result.get("var2").value());
assertEquals("123",result.getUriVariables().get("var1"));
assertEquals("456",result.getUriVariables().get("var2"));
// vars associated with second variable
assertNull(result.get("var1").parameters().get("a"));
assertNull(result.get("var1").parameters().get("c"));
assertEquals("[b]",result.get("var2").parameters().get("a").toString());
assertEquals("[d]",result.get("var2").parameters().get("c").toString());
assertNull(result.getMatrixVariables().get("var1"));
assertNull(result.getMatrixVariables().get("var1"));
assertEquals("b",result.getMatrixVariables().get("var2").getFirst("a"));
assertEquals("d",result.getMatrixVariables().get("var2").getFirst("c"));
// CaptureTheRestPathElement
result = matchAndExtract("/{*var}","/abc/123_456;a=b;c=d");
assertEquals("/abc/123_456",result.get("var").value());
assertEquals("[b]",result.get("var").parameters().get("a").toString());
assertEquals("[d]",result.get("var").parameters().get("c").toString());
assertEquals("/abc/123_456",result.getUriVariables().get("var"));
assertEquals("b",result.getMatrixVariables().get("var").getFirst("a"));
assertEquals("d",result.getMatrixVariables().get("var").getFirst("c"));
result = matchAndExtract("/{*var}","/abc/123_456;a=b;c=d/789;a=e;f=g");
assertEquals("/abc/123_456/789",result.get("var").value());
assertEquals("[b, e]",result.get("var").parameters().get("a").toString());
assertEquals("[d]",result.get("var").parameters().get("c").toString());
assertEquals("[g]",result.get("var").parameters().get("f").toString());
assertEquals("/abc/123_456/789",result.getUriVariables().get("var"));
assertEquals("[b, e]",result.getMatrixVariables().get("var").get("a").toString());
assertEquals("d",result.getMatrixVariables().get("var").getFirst("c"));
assertEquals("g",result.getMatrixVariables().get("var").getFirst("f"));
result = matchAndExtract("/abc/{var}","/abc/one");
assertEquals("one",result.get("var").value());
assertEquals(0,result.get("var").parameters().size());
assertEquals("one",result.getUriVariables().get("var"));
assertNull(result.getMatrixVariables().get("var"));
}
// ---
private Map<String, PathMatchResult> matchAndExtract(String pattern, String path) {
private PathMatchResult matchAndExtract(String pattern, String path) {
return parse(pattern).matchAndExtract(PathPatternMatcherTests.toPathContainer(path));
}
@ -1346,29 +1349,26 @@ public class PathPatternMatcherTests { @@ -1346,29 +1349,26 @@ public class PathPatternMatcherTests {
assertFalse(pattern.matches(PathContainer));
}
private Map<String, PathMatchResult> checkCapture(String uriTemplate, String path, String... keyValues) {
private PathMatchResult checkCapture(String uriTemplate, String path, String... keyValues) {
PathPatternParser parser = new PathPatternParser();
PathPattern pattern = parser.parse(uriTemplate);
Map<String, PathMatchResult> matchResults = pattern.matchAndExtract(toPathContainer(path));
PathMatchResult matchResult = pattern.matchAndExtract(toPathContainer(path));
Map<String, String> expectedKeyValues = new HashMap<>();
if (keyValues != null) {
for (int i = 0; i < keyValues.length; i += 2) {
expectedKeyValues.put(keyValues[i], keyValues[i + 1]);
}
for (int i = 0; i < keyValues.length; i += 2) {
expectedKeyValues.put(keyValues[i], keyValues[i + 1]);
}
Map<String, PathMatchResult> capturedVariables = matchResults;
for (Map.Entry<String, String> me : expectedKeyValues.entrySet()) {
String value = capturedVariables.get(me.getKey()).value();
String value = matchResult.getUriVariables().get(me.getKey());
if (value == null) {
fail("Did not find key '" + me.getKey() + "' in captured variables: "
+ capturedVariables);
+ matchResult.getUriVariables());
}
if (!value.equals(me.getValue())) {
fail("Expected value '" + me.getValue() + "' for key '" + me.getKey()
+ "' but was '" + value + "'");
}
}
return capturedVariables;
return matchResult;
}
private void checkExtractPathWithinPattern(String pattern, String path, String expected) {
@ -1399,7 +1399,7 @@ public class PathPatternMatcherTests { @@ -1399,7 +1399,7 @@ public class PathPatternMatcherTests {
return pattern.getPathRemaining(toPathContainer(path));
}
private Map<String, PathMatchResult> matchAndExtract(PathPattern p, String path) {
private PathMatchResult matchAndExtract(PathPattern p, String path) {
return p.matchAndExtract(toPathContainer(path));
}

20
spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java

@ -19,14 +19,18 @@ package org.springframework.web.util.pattern; @@ -19,14 +19,18 @@ package org.springframework.web.util.pattern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.http.server.reactive.PathContainer;
import org.springframework.web.util.pattern.PathPattern.PathMatchResult;
import org.springframework.web.util.pattern.PatternParseException.PatternMessage;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Exercise the {@link PathPatternParser}.
@ -137,23 +141,23 @@ public class PathPatternParserTests { @@ -137,23 +141,23 @@ public class PathPatternParserTests {
pathPattern = checkStructure("/{var:[^\\/]*}");
assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
Map<String, PathMatchResult> result = matchAndExtract(pathPattern,"/foo");
assertEquals("foo", result.get("var").value());
PathMatchResult result = matchAndExtract(pathPattern,"/foo");
assertEquals("foo", result.getUriVariables().get("var"));
pathPattern = checkStructure("/{var:\\[*}");
assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
result = matchAndExtract(pathPattern,"/[[[");
assertEquals("[[[", result.get("var").value());
assertEquals("[[[", result.getUriVariables().get("var"));
pathPattern = checkStructure("/{var:[\\{]*}");
assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
result = matchAndExtract(pathPattern,"/{{{");
assertEquals("{{{", result.get("var").value());
assertEquals("{{{", result.getUriVariables().get("var"));
pathPattern = checkStructure("/{var:[\\}]*}");
assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
result = matchAndExtract(pathPattern,"/}}}");
assertEquals("}}}", result.get("var").value());
assertEquals("}}}", result.getUriVariables().get("var"));
pathPattern = checkStructure("*");
assertEquals(WildcardPathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
@ -467,7 +471,7 @@ public class PathPatternParserTests { @@ -467,7 +471,7 @@ public class PathPatternParserTests {
assertFalse(pp.matches(PathPatternMatcherTests.toPathContainer(path)));
}
private Map<String, PathMatchResult> matchAndExtract(PathPattern pp, String path) {
private PathMatchResult matchAndExtract(PathPattern pp, String path) {
return pp.matchAndExtract(PathPatternMatcherTests.toPathContainer(path));
}

4
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java

@ -343,7 +343,7 @@ public abstract class RequestPredicates { @@ -343,7 +343,7 @@ public abstract class RequestPredicates {
boolean match = this.pattern.matches(path);
traceMatch("Pattern", this.pattern.getPatternString(), path, match);
if (match) {
mergeTemplateVariables(request, this.pattern.matchAndExtract(request.path()));
mergeTemplateVariables(request, this.pattern.matchAndExtract(request.path()).getUriVariables());
return true;
}
else {
@ -355,7 +355,7 @@ public abstract class RequestPredicates { @@ -355,7 +355,7 @@ public abstract class RequestPredicates {
public Optional<ServerRequest> nest(ServerRequest request) {
return Optional.ofNullable(this.pattern.getPathRemaining(request.path()))
.map(info -> {
mergeTemplateVariables(request, info.getMatchingVariables());
mergeTemplateVariables(request, info.getUriVariables());
String path = info.getPathRemaining();
if (!path.startsWith("/")) {
path = "/" + path;

8
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java

@ -119,7 +119,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -119,7 +119,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* the "other" instance as follows:
* <ul>
* <li>If there are patterns in both instances, combine the patterns in "this" with
* the patterns in "other" using {@link PathPattern#combine(String)}.
* the patterns in "other" using {@link PathPattern#combine(PathPattern)}.
* <li>If only one instance has patterns, use them.
* <li>If neither instance has patterns, use an empty String (i.e. "").
* </ul>
@ -130,8 +130,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -130,8 +130,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
for (PathPattern pattern1 : this.patterns) {
for (PathPattern pattern2 : other.patterns) {
String combinedPattern = pattern1.combine(pattern2.getPatternString());
combined.add(this.parser.parse(combinedPattern));
combined.add(pattern1.combine(pattern2));
}
}
}
@ -166,7 +165,8 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -166,7 +165,8 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value();
SortedSet<PathPattern> matches = getMatchingPatterns(lookupPath);
return matches.isEmpty() ? null : new PatternsRequestCondition(new ArrayList<>(matches), this.parser);
return matches.isEmpty() ? null : new PatternsRequestCondition(
new ArrayList<PathPattern>(matches), this.parser);
}
/**

80
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java

@ -17,18 +17,14 @@ @@ -17,18 +17,14 @@
package org.springframework.web.reactive.result.method;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
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;
@ -36,9 +32,7 @@ import org.springframework.http.HttpMethod; @@ -36,9 +32,7 @@ import org.springframework.http.HttpMethod;
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;
@ -113,31 +107,25 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -113,31 +107,25 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
PathPattern bestPattern;
Map<String, String> uriVariables;
Map<String, MultiValueMap<String, String>> matrixVariables;
Set<PathPattern> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = getPathPatternParser().parse(lookupPath);
uriVariables = Collections.emptyMap();
matrixVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
uriVariables = bestPattern.matchAndExtract(lookupPath);
}
// Let URI vars be stripped of semicolon content..
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(exchange, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
// Now decode URI variables
if (!uriVariables.isEmpty()) {
uriVariables = uriVariables.entrySet().stream().collect(Collectors.toMap(
Entry::getKey, e -> StringUtils.uriDecode(e.getValue(), StandardCharsets.UTF_8)
));
PathPattern.PathMatchResult result = bestPattern.matchAndExtract(lookupPath);
uriVariables = result.getUriVariables();
matrixVariables = result.getMatrixVariables();
}
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables);
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
@ -145,62 +133,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe @@ -145,62 +133,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
}
}
private Map<String, MultiValueMap<String, String>> extractMatrixVariables(
ServerWebExchange exchange, Map<String, String> uriVariables) {
Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<>();
for (Entry<String, String> uriVar : uriVariables.entrySet()) {
String uriVarValue = uriVar.getValue();
int equalsIndex = uriVarValue.indexOf('=');
if (equalsIndex == -1) {
continue;
}
String semicolonContent;
int semicolonIndex = uriVarValue.indexOf(';');
if ((semicolonIndex == -1) || (semicolonIndex == 0) || (equalsIndex < semicolonIndex)) {
semicolonContent = uriVarValue;
}
else {
semicolonContent = uriVarValue.substring(semicolonIndex + 1);
uriVariables.put(uriVar.getKey(), uriVarValue.substring(0, semicolonIndex));
}
result.put(uriVar.getKey(), parseMatrixVariables(exchange, semicolonContent));
}
return result;
}
private static MultiValueMap<String, String> parseMatrixVariables(ServerWebExchange exchange,
String semicolonContent) {
MultiValueMap<String, String> vars = new LinkedMultiValueMap<>();
if (!StringUtils.hasText(semicolonContent)) {
return vars;
}
StringTokenizer pairs = new StringTokenizer(semicolonContent, ";");
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)) {
vars.add(name, value);
}
}
else {
vars.add(pair, "");
}
}
MultiValueMap<String, String> decoded = new LinkedMultiValueMap<>(vars.size());
vars.forEach((key, values) -> values.forEach(value -> {
String decodedValue = StringUtils.uriDecode(value, StandardCharsets.UTF_8);
decoded.add(key, decodedValue);
}));
return decoded;
}
/**
* Iterate all RequestMappingInfos once again, look if any match by URL at
* least and raise exceptions accordingly.

2
spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerRequestTests.java

@ -50,7 +50,7 @@ import org.springframework.util.LinkedMultiValueMap; @@ -50,7 +50,7 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
/**

26
spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java

@ -294,41 +294,19 @@ public class RequestMappingInfoHandlerMappingTests { @@ -294,41 +294,19 @@ public class RequestMappingInfoHandlerMappingTests {
assertEquals(Arrays.asList("red", "blue", "green"), matrixVariables.get("colors"));
assertEquals("2012", matrixVariables.getFirst("year"));
assertEquals("cars", uriVariables.get("cars"));
exchange = get("/cars;colors=red,blue,green;year=2012").toExchange();
handleMatch(exchange, "/{cars:[^;]+}{params}");
matrixVariables = getMatrixVariables(exchange, "params");
uriVariables = getUriTemplateVariables(exchange);
assertNotNull(matrixVariables);
assertEquals(Arrays.asList("red", "blue", "green"), matrixVariables.get("colors"));
assertEquals("2012", matrixVariables.getFirst("year"));
assertEquals("cars", uriVariables.get("cars"));
assertEquals(";colors=red,blue,green;year=2012", uriVariables.get("params"));
exchange = get("/cars").toExchange();
handleMatch(exchange, "/{cars:[^;]+}{params}");
matrixVariables = getMatrixVariables(exchange, "params");
uriVariables = getUriTemplateVariables(exchange);
assertNull(matrixVariables);
assertEquals("cars", uriVariables.get("cars"));
assertEquals("", uriVariables.get("params"));
}
@Test
public void handleMatchMatrixVariablesDecoding() throws Exception {
ServerWebExchange exchange = method(HttpMethod.GET, URI.create("/path;mvar=a%2fb")).toExchange();
handleMatch(exchange, "/path{filter}");
handleMatch(exchange, "/{filter}");
MultiValueMap<String, String> matrixVariables = getMatrixVariables(exchange, "filter");
Map<String, String> uriVariables = getUriTemplateVariables(exchange);
assertNotNull(matrixVariables);
assertEquals(Collections.singletonList("a/b"), matrixVariables.get("mvar"));
assertEquals(";mvar=a/b", uriVariables.get("filter"));
assertEquals("path", uriVariables.get("filter"));
}

Loading…
Cancel
Save