Browse Source

Document PathPattern matching for single/multiple segments

This commit improves the reference document to better reflect the
different between `*` or `{name}` on one side, and `**` or `{*path}` on
the other.

The former patterns only consider a single path segment and its content,
while the latter variants consider zero or more path segments. This
explains why `/test/{*path}` can match `/test`.

Closes gh-35727
pull/35814/head
Brian Clozel 1 month ago
parent
commit
8bb63081a8
  1. 55
      framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc
  2. 56
      framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc
  3. 57
      framework-docs/modules/ROOT/partials/web/uri-patterns.adoc
  4. 4
      spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java

55
framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc

@ -89,60 +89,7 @@ Kotlin::
You can map requests by using glob patterns and wildcards: You can map requests by using glob patterns and wildcards:
[cols="2,3,5"] include::partial$web/uri-patterns.adoc[leveloffset=+1]
|===
|Pattern |Description |Example
| `spring`
| Literal pattern
| `+"/spring"+` matches `+"/spring"+`
| `+?+`
| Matches one character
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`
| `+*+`
| Matches zero or more characters within a path segment
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`
`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`
| `+**+`
| Matches zero or more path segments
| `+"/resources/**"+` matches `+"/resources/file.png"+` and `+"/resources/images/file.png"+`
`+"/**/resources"+` matches `+"/spring/resources"+` and `+"/spring/framework/resources"+`
`+"/resources/**/file.png"+` is invalid as `+**+` is not allowed in the middle of the path.
`+"/**/{name}/resources"+` is invalid as only a literal pattern is allowed right after `+**+`.
`+"/**/project/{project}/resources"+` is allowed.
`+"/**/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
| `+{name}+`
| Matches a path segment and captures it as a variable named "name"
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`
`+"/projects/{project}/versions"+` does not match `+"/projects/spring/framework/versions"+` as it captures a single path segment.
| `{name:[a-z]+}`
| Matches the regexp `[a-z]+` as a path variable named "name"
| `/projects/{project:[a-z]+}/versions` matches `/projects/spring/versions` but not `/projects/spring1/versions`
| `+{*path}+`
| Matches zero or more path segments and captures it as a variable named "path"
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`
`+"{*path}/resources"+` matches `+"/spring/framework/resources"+` and captures `+path=/spring/framework+`
`+"/resources/{*path}/file.png"+` is invalid as `{*path}` is not allowed in the middle of the path.
`+"/{*path}/{name}/resources"+` is invalid as only a literal pattern is allowed right after `{*path}`.
`+"/{*path}/project/{project}/resources"+` is allowed.
`+"/{*path}/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
|===
Captured URI variables can be accessed with `@PathVariable`, as the following example shows: Captured URI variables can be accessed with `@PathVariable`, as the following example shows:

56
framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc

@ -105,61 +105,7 @@ customizations of path matching options.
You can map requests by using glob patterns and wildcards: You can map requests by using glob patterns and wildcards:
[cols="2,3,5"] include::partial$web/uri-patterns.adoc[leveloffset=+1]
|===
|Pattern |Description |Example
| `spring`
| Literal pattern
| `+"/spring"+` matches `+"/spring"+`
| `+?+`
| Matches one character
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`
| `+*+`
| Matches zero or more characters within a path segment
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`
`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`
| `+**+`
| Matches zero or more path segments
| `+"/resources/**"+` matches `+"/resources/file.png"+` and `+"/resources/images/file.png"+`
`+"/**/resources"+` matches `+"/spring/resources"+` and `+"/spring/framework/resources"+`
`+"/resources/**/file.png"+` is invalid as `+**+` is not allowed in the middle of the path.
`+"/**/{name}/resources"+` is invalid as only a literal pattern is allowed right after `+**+`.
`+"/**/project/{project}/resources"+` is allowed.
`+"/**/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
| `+{name}+`
| Matches a path segment and captures it as a variable named "name"
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`
`+"/projects/{project}/versions"+` does not match `+"/projects/spring/framework/versions"+` as it captures a single path segment.
| `{name:[a-z]+}`
| Matches the regexp `"[a-z]+"` as a path variable named "name"
| `"/projects/{project:[a-z]+}/versions"` matches `"/projects/spring/versions"` but not `"/projects/spring1/versions"`
| `+{*path}+`
| Matches zero or more path segments and captures it as a variable named "path"
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`
`+"{*path}/resources"+` matches `+"/spring/framework/resources"+` and captures `+path=/spring/framework+`
`+"/resources/{*path}/file.png"+` is invalid as `{*path}` is not allowed in the middle of the path.
`+"/{*path}/{name}/resources"+` is invalid as only a literal pattern is allowed right after `{*path}`.
`+"/{*path}/project/{project}/resources"+` is allowed.
`+"/{*path}/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
|===
Captured URI variables can be accessed with `@PathVariable`. For example: Captured URI variables can be accessed with `@PathVariable`. For example:

57
framework-docs/modules/ROOT/partials/web/uri-patterns.adoc

@ -0,0 +1,57 @@
[cols="2,3,5"]
|===
|Pattern |Description |Example
| `spring`
| Literal pattern
| `+"/spring"+` matches `+"/spring"+`
| `+?+`
| Matches one character
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`
| `+*+`
| Matches zero or more characters within a path segment
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`
`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`.
`+"/projects/*"+` matches `+"/projects/spring"+` but does not match `+"/projects"+` as the path segment is not present.
| `+**+`
| Matches zero or more path segments
| `+"/resources/**"+` matches `+"/resources"+`, `+"/resources/file.png"+` and `+"/resources/images/file.png"+`
`+"/**/info"+` matches `+"/info"+`, `+"/spring/info"+` and `+"/spring/framework/info"+`
`+"/resources/**/file.png"+` is invalid as `+**+` is not allowed in the middle of the path.
`+"/**/{name}/resources"+` is invalid as only a literal pattern is allowed right after `+**+`.
`+"/**/project/{project}/resources"+` is allowed.
`+"/**/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
| `+{name}+`
| Similar to `+*+`, but also captures the path segment as a variable named "name"
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`
`+"/projects/{project}/versions"+` does not match `+"/projects/spring/framework/versions"+` as it captures a single path segment.
| `{name:[a-z]+}`
| Matches the regexp `"[a-z]+"` as a path variable named "name"
| `"/projects/{project:[a-z]+}/versions"` matches `"/projects/spring/versions"` but not `"/projects/spring1/versions"`
| `+{*path}+`
| Similar to `+**+`, but also captures the path segments as a variable named "path"
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`
`+"{*path}/resources"+` matches `+"/spring/framework/resources"+` and captures `+path=/spring/framework+`
`+"/resources/{*path}/file.png"+` is invalid as `{*path}` is not allowed in the middle of the path.
`+"/{*path}/{name}/resources"+` is invalid as only a literal pattern is allowed right after `{*path}`.
`+"/{*path}/project/{project}/resources"+` is allowed.
`+"/{*path}/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
|===

4
spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java

@ -149,14 +149,14 @@ class PathPatternTests {
checkMatches("/*/bar", "/foo/bar"); checkMatches("/*/bar", "/foo/bar");
checkNoMatch("/*/bar", "/foo/baz"); checkNoMatch("/*/bar", "/foo/baz");
checkNoMatch("/*/bar", "//bar"); checkNoMatch("/*/bar", "//bar");
checkNoMatch("/*/bar", "/bar");
checkMatches("/f*/bar", "/foo/bar"); checkMatches("/f*/bar", "/foo/bar");
checkMatches("/*/bar", "/foo/bar");
checkMatches("a/*","a/"); checkMatches("a/*","a/");
checkMatches("/*","/"); checkMatches("/*","/");
checkMatches("/*/bar", "/foo/bar"); checkMatches("/*/bar", "/foo/bar");
checkNoMatch("/*/bar", "/foo/baz"); checkNoMatch("/*/bar", "/foo/baz");
checkNoMatch("/*/bar", "/bar");
checkMatches("/f*/bar", "/foo/bar"); checkMatches("/f*/bar", "/foo/bar");
checkMatches("/*/bar", "/foo/bar");
checkMatches("/a*b*c*d/bar", "/abcd/bar"); checkMatches("/a*b*c*d/bar", "/abcd/bar");
checkMatches("*a*", "testa"); checkMatches("*a*", "testa");
checkMatches("a/*", "a/"); checkMatches("a/*", "a/");

Loading…
Cancel
Save