Browse Source

Allow separator configuration in PathPatternParser

This commit allows to configure a custom path separator when parsing and
matching path patterns with `PathPatternParser`, but also when parsing
incoming paths as `PathContainer` instances.

Closes gh-23092
pull/23098/head
Brian Clozel 6 years ago
parent
commit
33becd8258
  1. 8
      spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java
  2. 18
      spring-web/src/main/java/org/springframework/http/server/PathContainer.java
  3. 24
      spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java
  4. 20
      spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java
  5. 9
      spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java

8
spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -89,11 +89,13 @@ final class DefaultPathContainer implements PathContainer {
} }
static PathContainer createFromUrlPath(String path) { static PathContainer createFromUrlPath(String path, String separator) {
if (path.equals("")) { if (path.equals("")) {
return EMPTY_PATH; return EMPTY_PATH;
} }
String separator = "/"; if (separator.length() == 0) {
throw new IllegalArgumentException("separator should not be empty");
}
Separator separatorElement = separator.equals(SEPARATOR.value()) ? SEPARATOR : () -> separator; Separator separatorElement = separator.equals(SEPARATOR.value()) ? SEPARATOR : () -> separator;
List<Element> elements = new ArrayList<>(); List<Element> elements = new ArrayList<>();
int begin; int begin;

18
spring-web/src/main/java/org/springframework/http/server/PathContainer.java

@ -65,14 +65,26 @@ public interface PathContainer {
} }
/**
* Parse the path value into a sequence of {@code "/"} {@link Separator Separator}
* and {@link PathSegment PathSegment} elements.
* @param path the encoded, raw path value to parse
* @return the parsed path
*/
static PathContainer parsePath(String path) {
return DefaultPathContainer.createFromUrlPath(path, "/");
}
/** /**
* Parse the path value into a sequence of {@link Separator Separator} and * Parse the path value into a sequence of {@link Separator Separator} and
* {@link PathSegment PathSegment} elements. * {@link PathSegment PathSegment} elements.
* @param path the encoded, raw URL path value to parse * @param path the encoded, raw path value to parse
* @param separator the decoded separator for parsing patterns
* @return the parsed path * @return the parsed path
* @since 5.2
*/ */
static PathContainer parsePath(String path) { static PathContainer parsePath(String path, String separator) {
return DefaultPathContainer.createFromUrlPath(path); return DefaultPathContainer.createFromUrlPath(path, separator);
} }

24
spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,6 +36,8 @@ public class PathPatternParser {
private boolean caseSensitive = true; private boolean caseSensitive = true;
private char separator = '/';
/** /**
* Whether a {@link PathPattern} produced by this parser should should * Whether a {@link PathPattern} produced by this parser should should
@ -75,14 +77,20 @@ public class PathPatternParser {
} }
/** /**
* Accessor used for the separator to use. * Char that represents the separator to use in the patterns.
* <p>Currently not exposed for configuration with URI path patterns and * <p>The default is {@code '/'}.
* mainly for use in InternalPathPatternParser and PathPattern. If required * @since 5.2
* in the future, a similar option would also need to be exposed in */
* {@link org.springframework.http.server.PathContainer PathContainer}. public void setSeparator(char separator) {
this.separator = separator;
}
/**
* Char that represents the separator to use in the patterns.
* @since 5.2
*/ */
char getSeparator() { public char getSeparator() {
return '/'; return this.separator;
} }

20
spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java

@ -27,6 +27,7 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/** /**
* Unit tests for {@link DefaultPathContainer}. * Unit tests for {@link DefaultPathContainer}.
@ -111,15 +112,18 @@ public class DefaultPathContainerTests {
testPath("//%20/%20", "//%20/%20", Arrays.asList("/", "/", "%20", "/", "%20")); testPath("//%20/%20", "//%20/%20", Arrays.asList("/", "/", "%20", "/", "%20"));
} }
private void testPath(String input, String value, List<String> expectedElements) { private void testPath(String input, String separator, String value, List<String> expectedElements) {
PathContainer path = PathContainer.parsePath(input, separator);
PathContainer path = PathContainer.parsePath(input);
assertThat(path.value()).as("value: '" + input + "'").isEqualTo(value); assertThat(path.value()).as("value: '" + input + "'").isEqualTo(value);
assertThat(path.elements().stream() assertThat(path.elements().stream()
.map(PathContainer.Element::value).collect(Collectors.toList())).as("elements: " + input).isEqualTo(expectedElements); .map(PathContainer.Element::value).collect(Collectors.toList())).as("elements: " + input).isEqualTo(expectedElements);
} }
private void testPath(String input, String value, List<String> expectedElements) {
testPath(input, "/", value, expectedElements);
}
@Test @Test
public void subPath() throws Exception { public void subPath() throws Exception {
// basic // basic
@ -137,4 +141,14 @@ public class DefaultPathContainerTests {
assertThat(path.subPath(2).value()).isEqualTo("/b/"); assertThat(path.subPath(2).value()).isEqualTo("/b/");
} }
@Test
public void pathWithCustomSeparator() throws Exception {
testPath("a.b.c", ".", "a.b.c", Arrays.asList("a", ".", "b", ".", "c"));
}
@Test
public void emptySeparator() {
assertThatIllegalArgumentException().isThrownBy(() -> PathContainer.parsePath("path", ""));
}
} }

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

@ -408,6 +408,15 @@ public class PathPatternParserTests {
assertThat(patterns.get(1)).isEqualTo(p2); assertThat(patterns.get(1)).isEqualTo(p2);
} }
@Test
public void separatorTests() {
PathPatternParser parser = new PathPatternParser();
parser.setSeparator('.');
String rawPattern = "first.second.{last}";
PathPattern pattern = parser.parse(rawPattern);
assertThat(pattern.computePatternString()).isEqualTo(rawPattern);
}
private PathPattern parse(String pattern) { private PathPattern parse(String pattern) {
PathPatternParser patternParser = new PathPatternParser(); PathPatternParser patternParser = new PathPatternParser();
return patternParser.parse(pattern); return patternParser.parse(pattern);

Loading…
Cancel
Save