tomcat關於配置servlet的url-pattern的問題詳解

tomcat在配置web.xml的時候,servlet是一個比較重要的問題,在這裡討論一下servlet中的幾個痛點

  1. servlet url-pattern的匹配問題
  2. url-pattern中//*的區別
  3. url-pattern的優先級問題
  4. 根路徑/的匹配問題

1 servlet url-pattern的匹配問題

url-pattern有三種匹配模式,分別是路徑匹配、精確匹配、後綴匹配

1.1 精確匹配

<url-pattern>中配置的項必須與url完全精確匹配。

代碼舉例👇

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/kata/detail.html</url-pattern>
    <url-pattern>/demo.html</url-pattern>
    <url-pattern>/table</url-pattern>
</servlet-mapping>

當在瀏覽器中輸入如下幾種url時,都會被匹配到該servlet
//10.43.11.143/myapp/kata/detail.html
//10.43.11.143/myapp/demo.html
//10.43.11.143/myapp/table

注意:

//10.43.11.143/myapp/table/ 是非法的url,不會被當作//10.43.11.143/myapp/table識別

另外上述url後面可以跟任意的查詢條件,都會被匹配,如

//10.43.11.143/myapp/table?hello 這個請求就會被匹配到MyServlet。

1.2 路徑匹配

以「/」字符開頭,並以「/*」結尾的字符串用於路徑匹配

代碼舉例👇

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/*</url-pattern>
</servlet-mapping>

路徑以/user/開始,後面的路徑可以任意。比如下面的url都會被匹配。

//localhost:8080/appDemo/user/users.html

//localhost:8080/appDemo/user/addUser.action

//localhost:8080/appDemo/user/updateUser.actionl

1.3 後綴匹配

以「*.」開頭的字符串被用於後綴匹配

代碼舉例👇

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

則任何擴展名為jsp或action的url請求都會匹配,比如下面的url都會被匹配

//localhost:8080/appDemo/user/users.jsp

//localhost:8080/appDemo/toHome.action

注意:路徑和後綴匹配無法同時設置

注意:路徑和擴展名匹配無法同時設置,比如下面的三個都是非法的,如果設置,啟動tomcat服務器會報錯。

<url-pattern>/kata/*.jsp</url-pattern>

<url-pattern>/*.jsp</url-pattern>

<url-pattern>he*.jsp</url-pattern>

幾個實例👇,不明白請看本文第三章

2 url-pattern中//*的區別

<url-pattern>/</url-pattern>

<url-pattern>/*</url-pattern>

先說/*/*相對來講比較好理解,它是路徑匹配的一種,從範圍上來講,它是範圍最廣的路徑匹配,所有的請求都符合它的要求,從精度上來講,它是精度最低的路徑匹配(注意!我說的是路徑匹配),路徑匹配的優先級是從長到短的(具體請看本文第三章),所以說它是精度最低的路徑匹配。很多博客中說它的特點是匹配*.jsp,這不是廢話嗎? /*本身就是路徑匹配,它當然可以匹配*.jsp

再說/,/是匹配優先級最低的匹配,當一個url和所有的url-pattern匹配都不合適的時候,這個url就會走/匹配,根本就沒有什麼*.jsp的限制,大家之所以產生了(客觀上也確實是這樣)/不會匹配*.jsp但是/*會匹配*.jsp的原因是在tomcat/conf/web.xml裏面單獨配置了*.jsp的配置,具體請看本文第三章

3 url-pattern的優先級問題

當一個url與多個servlet的匹配規則可以匹配時,則按照 「 精確路徑 > 最長路徑>後綴匹配」這樣的優先級匹配到對應的servlet。

例1:比如servletA 的url-pattern為 /test,servletB的url-pattern為 /* ,這個時候,如果我訪問的url為//localhost/test ,這個時候容器就會先進行精確路徑匹配,發現/test正好被servletA精確匹配,那麼就去調用servletA,不會去管servletB。

例2:比如servletA的url-pattern為/test/,而servletB的url-pattern為/test/a/,此時訪問//localhost/test/a時,容器會選擇路徑最長的servlet來匹配,也就是這裡的servletB。

例3: 比如servletA的url-pattern:*.action ,servletB的url-pattern為 / *,這個時候,如果我訪問的url為//localhost/test.action,這個時候容器就會優先進行路徑匹配,而不是去匹配擴展名,這樣就去調用servletB。

**那麼就產生了一個疑問。為什麼/*會匹配到 .jsp,但是/匹配不到 .jsp呢?

原因很簡單,在tomcat/conf/web.xml裏面會有如下配置

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

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

👆可以清楚地看到 *.jsp作為名為jsp的servlet的後綴匹配,/*是路徑匹配,其優先級高於後綴匹配,所以能匹配到後綴為jsp的文件。而/是級別最低的匹配,其級別低於後綴匹配,所以jsp文件不會被url-pattern為/的匹配到。

4 根路徑/的匹配問題

大家應該會注意到一個問題,就是當url-pattern為/*的時候訪問//localhost:8080/會404,但是訪問//localhost:8080/index.html卻沒有問題(當然前提是在spring容器裏面配置了<mvc:default-servlet-handler/>)。當url-pattern為/時,//localhost:8080/ 會自動轉發到 //localhost:8080/index.html 而不會404。原因是什麼呢?

首先,我們必須要明確,一個網址的根目錄即/(比如//localhost:8080/)到底意味着什麼?經過實驗發現/是很特殊的,它會被url-pattern為/*的匹配,但他不會被url-pattern為/匹配。

在tomcat中,/默認是屬於會被defaultservlet匹配,但是其優先級低於路徑匹配,所以當某一個servlet的url-pattern為/*時,/就會被這個servlet匹配,從而不被defaultservlet匹配。

在tomcat源代碼中找到如下片段可以佐證我的看法👇

<!-- ==================== Default Welcome File List ===================== -->
<!-- When a request URI refers to a directory, the default servlet looks  -->
<!-- for a "welcome file" within that directory and, if present, to the   -->
<!-- corresponding resource URI for display.                              -->
<!-- If no welcome files are present, the default servlet either serves a -->
<!-- directory listing (see default servlet configuration on how to       -->
<!-- customize) or returns a 404 status, depending on the value of the    -->
<!-- listings setting.                                                    -->
<!--                                                                      -->
<!-- If you define welcome files in your own application's web.xml        -->
<!-- deployment descriptor, that list *replaces* the list configured      -->
<!-- here, so be sure to include any of the default values that you wish  -->
<!-- to use within your application.                                       -->

👆上面是講 Welcome File List的,即/路徑會被默認轉發到Welcome File List中規定的網頁,即初始頁。我翻譯一下上面的一部分,具體的可以谷歌翻譯,翻譯👉

翻譯👇

當請求URI指向目錄時,默認servlet在該目錄中查找「歡迎文件」,如果存在,則在相應的資源URI中查找以進行顯示。如果不存在歡迎文件,則默認servlet會提供目錄列表(請參閱默認servlet配置中的有關如何自定義的內容)或返回404狀態,具體取決於列表設置的值

/會重定向到歡迎頁面的原因是Welcome File List的存在,Welcome File List發揮效果的前提是/必須被defaultservlet匹配。當某一個servlet的url-pattern為/*時,/就會被這個servlet匹配,從而不被defaultservlet匹配。所以只有在自己定義的servlet的url-pattern為/時,//localhost:8080/ 會自動轉發到 //localhost:8080/index.html 而不會404