路径匹配
Servlet API 将完整的请求路径暴露为 requestURI
,并进一步将其细分为 contextPath
、servletPath
和 pathInfo
,它们的值根据 Servlet 的映射方式而异。Spring MVC 需要根据这些输入确定用于映射 handler 的查找路径,该路径应排除 contextPath
和任何适用的 servletMapping
前缀。
servletPath
和 pathInfo
会被解码,这使得它们无法与完整的 requestURI
直接进行比较以派生查找路径,因此需要对 requestURI
进行解码。然而,这带来了自身的问题,因为路径可能包含编码的保留字符,例如 "/"
或 ";"
,它们在解码后会改变路径结构,这也会导致安全问题。此外,Servlet 容器可能会对 servletPath
进行不同程度的规范化,这使得执行针对 requestURI
的 startsWith
比较变得更加困难。
这就是为什么最好避免依赖附带基于前缀的 servletPath
映射类型的 servletPath
。如果 DispatcherServlet
被映射为默认 Servlet(使用 "/"
)或未使用前缀(使用 "/*"
),并且 Servlet 容器是 4.0+,那么 Spring MVC 能够检测到 Servlet 映射类型并完全避免使用 servletPath
和 pathInfo
。在 3.1 Servlet 容器上,假设使用相同的 Servlet 映射类型,可以通过在 MVC 配置中通过 路径匹配 提供 UrlPathHelper
并设置 alwaysUseFullPath=true
来实现等效效果。
幸运的是,默认的 Servlet 映射 "/"
是一个不错的选择。然而,仍然存在一个问题,即需要对 requestURI
进行解码才能与控制器映射进行比较。这再次是不可取的,因为它可能解码保留字符从而改变路径结构。如果不需要此类字符,则可以拒绝它们(例如 Spring Security HTTP 防火墙),或者可以将 UrlPathHelper
配置为 urlDecode=false
,但这要求控制器映射必须与编码的路径匹配,这并非总是可行。此外,有时 DispatcherServlet
需要与另一个 Servlet 共享 URL 空间,并可能需要通过前缀进行映射。
使用 PathPatternParser
和解析后的模式可以解决上述问题,作为使用 AntPathMatcher
进行字符串路径匹配的替代方案。从 5.3 版本开始,PathPatternParser
已可在 Spring MVC 中使用,并从 6.0 版本开始默认启用。与需要解码查找路径或编码控制器映射的 AntPathMatcher
不同,解析后的 PathPattern
会与路径的解析表示形式(称为 RequestPath
)进行匹配,每次匹配一个路径段。这允许单独解码和净化路径段值,而不会改变路径结构。解析后的 PathPattern
还支持使用 servletPath
前缀映射,只要使用了 Servlet 路径映射且前缀保持简单(即没有编码字符)。有关模式语法详细信息和比较,请参阅 模式比较。