请求路径映射

请求路径映射

参考博客:
https://www.jianshu.com/p/817f5f83d916

基本定义

RequestURI:请求路径

返回 HTTP 协议报文的第一行中的路径部分中,从协议名称(不包含)开始到查询参数(不包含)的部分。

HTTP 协议报文的第一行 RequestURI 备注
POST /some/path.html HTTP/1.1 /some/path.html
GET http://foo.bar/a.html HTTP/1.0 /a.html foo.bar 是域名
HEAD /xyz?a=b HTTP/1.1 /xyz
如果排除 URL 参数中的 ; 的影响
RequestURI = ContextPath + ServletPath + PathInfo

ContextPath:上下文路径,

返回 RequestURI 中表示请求的上下文的部分,即对应的 ServletContext 关联的路径前缀。若该上下文是处于服务器基地址的默认上下文,则这个路径是空串,这种情况请参考 Tomcat 的 Context 配置文档 Apache Tomcat 8 Configuration Reference (8.5.82) - The Context Container 中的 Naming 一节。否则,如果这个上下文不处在服务器的根,则这个路径会以 / 开头但不会以 / 结尾。    
servlet 容器(比如 Tomcat)可能通过多个上下文路径匹配一个实际的应用上下文。在这种情况下,该方法将返回当前请求使用的实际上下文路径,它可能与 ServletContext#getContextPath 方法返回的路径不同。应该将 ServletContext#getContextPath 返回的上下文路径视为应用程序的首选上下文路径。

ServletPath:Servlet 路径

返回这个请求的 URL 中调用(匹配)servlet 的那一部分。该路径一般以“/”字符开头,包含 servlet 名称或到 servlet 的路径,但不包含任何额外的路径信息(比如 ContextPath 上下文路径)或查询字符串(query parameter,以 开头以 & 分割的参数)。
当请求与 /* 或空串两种模式匹配时是 ServletPath 都是空串

PathInfo

PathInfo 定义为请求路径中既不是 Context Path 也不是 Servlet Path 的部分,它要么是 null,这是没有额外路径的情况下,要么是以/开头的字符串。

Web 应用服务器中的路径映射

在收到客户端的请求后,由 Web 容器(比如 Tomcat)来决定向哪个 Web 应用(Web Application)转发该请求。所选择的 Web 应用一定有与请求 URL 从起始处开始相匹配的最长的 ContextPath。URL 中匹配的部分就是映射到 servlet 时的 ContextPath。
映射到 servlet 时使用的路径是请求对象中的请求 URL 除去 ContextPath 和查询参数路径参数(query parameter,以 开头以 & 分割的参数)。即 ServletPath
下面的 URL 路径映射规则需要按顺序使用,第一个匹配后便不再尝试其他匹配

  1. 容器尝试查找请求路径与 servlet 的精确匹配;
  2. 容器会递归地尝试匹配最长路径前缀。以 / 为分隔符,在路径树中一次步进一个目录。最长的匹配会最终胜出;/* 或者 /** 路径的匹配在这一层生效。
  3. 如果 URL 路径中的最后一段包含扩展名(如.jsp),那么容器会尝试匹配能处理扩展名的 servlet。扩展名定义为最后一段中最后的点号 . 之后的部分;
  4. 如果前三个规则没有成功匹配,容器会尝试去提供请求路径对应的资源(Tomcat 提供的默认的 Servlet 的功能),许多容器都提供了隐式的默认 servlet。如果应用自己定义了默认 servlet,则它会被使用。/ 的匹配在这一层生效。

    映射路径为 / 的就是默认的 Servlet,而不是指只某个特定类名的 Servlet,为什么这么说呢,因为根据第二条匹配规则,/ 可以跟任何路径匹配,但是它最短,所以只能最后匹配,所以它是默认的兜底的 Servlet。

Web.xml - Servlet 的映射匹配路径声明

Web 应用部署描述符(Deployment Descriptor,即 Web.xml)使用如下规则定义映射:

  1. / 开头并以 /* 结尾的字符串用于路径映射;
  2. *. 前缀开头的字符串用于扩展名映射;
  3. 空串 "" 是特殊的模式,精确地映射到应用上下文的根,举例来说,对来自 http://host:port/<context-root>/ 的请求,PathInfo 是 /,ServletPath 和 ContextPath 都是空串 "";
  4. 只包含 / 的字符串表明此 Servlet 是此应用的默认 servlet,这种情况下 ServletPath 是请求 URI 减去 ContextPath,PathInfo 是 null;
  5. 其他字符串只会精确匹配。

实践

在 Web.xml 中

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

访问 /SpringMVC_AnnotationConfig/aaa,结果为:

分析:
根据 Web 应用服务器中的路径映射,/SpringMVC_AnnotationConfig 的 Web 应用能与 /SpringMVC_AnnotationConfig/aaa 最长匹配,匹配的部分 /SpringMVC_AnnotationConfig 即是 ContextPath;映射到 servlet 时使用的路径是 /SpringMVC_AnnotationConfig/aaa 减去 /SpringMVC_AnnotationConfig,所以 /aaa 会用于 Servlet 映射路径匹配,ServletPath 一节,与 /* 匹配时,ServletPath 为 "";
根据等式规则可得 PathInfo 为 /aaa
此时将 <url-pattern 中的 /* 改成 /

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

访问 /SpringMVC_AnnotationConfig/aaa,结果为:

跟之前不一样,分析:
根据 Web 应用服务器中的路径映射,/SpringMVC_AnnotationConfig 的 Web 应用能与 /SpringMVC_AnnotationConfig/aaa 最长匹配,匹配的部分 /SpringMVC_AnnotationConfig 即是 ContextPath;映射到 servlet 时使用的路径是 /SpringMVC_AnnotationConfig/aaa 减去 /SpringMVC_AnnotationConfig,所以 /aaa 会用于 Servlet 映射路径匹配,但是此时用户只声明了一个映射路径为 / 的 Servlet(默认的 Servlet),根据 Web 应用服务器中的路径映射的第四条规则,匹配到此 Servlet,因此 ServletPath 为 /aaa,PathInfo 为 null。
那什么情况下 ServletPath 和 PathInfo 都有值呢?设置 Servlet 的映射路径为 /Spring/*

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/Spring/*</url-pattern>
</servlet-mapping>

访问 /SpringMVC_AnnotationConfig/Spring/aaa,结果为:

可以看到 ServletPath 为 /Spring,PathInfo 为 /aaa。分析如下:
根据 Web 应用服务器中的路径映射,/SpringMVC_AnnotationConfig 的 Web 应用能与 /SpringMVC_AnnotationConfig/Spring/aaa 最长匹配,匹配的部分 /SpringMVC_AnnotationConfig 即是 ContextPath;映射到 servlet 时使用的路径是 /SpringMVC_AnnotationConfig/Spring/aaa 减去 /SpringMVC_AnnotationConfig,所以 /Spring/aaa 会用于 Servlet 映射路径匹配,但是此时用户只声明了一个映射路径为 /Spring/* 的 Servlet,根据 Web 应用服务器中的路径映射的第 2 条规则,匹配到此 Servlet,同时因为第二节路径是通过 /* 匹配的,而通过 /* 匹配的最终都为空字符串(ServletPath 小节提到过),因此,ServletPath 为 /Spring,剩下的就是 PathInfo,所以 PathInfo 为 /aaa

从这个例子中,我们也能看出 PathInfo 这个概念的意义,这个概念就是为了存放 /* 到底匹配了什么

实际应用

在 web 过滤器和 spring 拦截器中

TODO
这个内容,在之前也学到过 《Servlet3.1-Specification.docx》
汇总过来。
《Servlet3.1-Specification.docx》 小节中的请求路径元素小节中的信息弄过来。