天天看點

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

問題描述

優付OMS站點,本地在IDEA裡用jetty9.4.*啟動程式時,浏覽器通路效果如下,頁面錯亂。

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

而把程式部署到測試環境是可以正常顯示的。測試環境的web伺服器是tomcat。

然後,本地用tomcat來啟動程式,發現在浏覽器裡也是可以正常通路的。

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

經排查,點選左側導航菜單打開功能頁是如下這樣實作的。可以看到,路徑上存在雙斜杠//,即比正常路徑多了一個斜杠/。

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

同樣的代碼,為什麼tomcat正常,而jetty卻不正常呢?

問題定位

我們接下來直接基于這個功能頁的url來排查。首先,不帶雙斜杠的url(​​http://localhost:8080/PCBossMgr/soho/showSaleInfoPage.html​​​)在jetty或tomcat下都是ok的。是以,我們的關注點聚焦在帶雙斜杠的url(​​http://localhost:8080/PCBossMgr//soho/showSaleInfoPage.html​​)上。

本地伺服器為jetty9.4.*時,浏覽器網絡請求如下。其中,Network裡第一個請求​​http://localhost:8080/PCBossMgr//soho/showSaleInfoPage.html​​​請求的是spring web controller的action方法,正常傳回200;而之後的靜态檔案,如css、js,則傳回了404。從請求位址 ​​http://localhost:8080/PCBossMgr//layui/css/index.css​​​、​​http://localhost:8080/PCBossMgr//layui/layui.js​​裡可以看到,這些url也是多了一個/,卻無法正常通路。

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

再看tomcat的網絡請求,無論是請求背景的action,還是靜态的css、js資源檔案,都是200。

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

靜态資源url裡多了斜杠"/",為什麼用tomcat可以正常通路,而用jetty卻404呢?

看來,tomcat與jetty兩個web容器對url的處理方式不同。

排查結果

我找來最有技術靈性的小王傑同學一起排查原因。如下是最終的分析結果。

URI的RFC3986規範裡,對請求路徑(​​https://datatracker.ietf.org/doc/html/rfc3986#section-3.3​​,章節3.3 Path)有如下說明:

If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character. If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//"). In addition, a URI reference (Section 4.1) may be a relative-path reference, in which case the first path segment cannot contain a colon (":") character. The ABNF requires five separate rules to disambiguate these cases, only one of which will match the path substring within a given URI reference. We use the generic term "path component" to describe the URI substring matched by the parser to one of these rules.

翻譯為:

如果URI包含權限元件,則路徑元件必須為空或以斜杠(“/”)字元開頭。如果URI不包含授權元件,則路徑不能以兩個斜杠字元(“/”)開頭。此外,URI引用(第4.1節)可以是相對路徑引用,在這種情況下,第一個路徑段不能包含冒号(“:”)字元。ABNF需要五個獨立的規則來消除這些情況的歧義,其中隻有一個規則将比對給定URI引用中的路徑子字元串。我們使用通用術語“路徑元件”來描述由解析器比對到這些規則之一的URI子字元串。

就是說,

通常情況下,RFC3986規範不允許url路徑裡包含兩個斜杠“//”。

在stackoverflow社群發現這麼一篇文章,​​Is a URL with // in the path-section valid? ​​文末回帖堪稱精辟:yes, it is valid, no, don't use it.

再來說jetty,jetty嚴格遵守了RFC3986規範。也就是說,jetty不允許url裡帶兩個斜杠,它會認為帶有//的url是模棱兩可的路徑(ambiguous empty segment)。怎麼講?假如controller的action方法映射的路徑有xxx/{var}/someurl,web靜态目錄裡也有xxx/somedir/somefile,那麼,當你通路url包含xxx//x的時候,jetty無法做出選擇。是以,jetty直接來了個痛快的,不支援這種形式的url,以免造成歧義。

tomcat呢,tomcat “違背規範” ,tomcat直接把請求路徑裡包含的多個連續的斜杠替換成單個的斜杠,比如 xxx//someurl 會被替換為 xxx/someurl。況且實際在我們的web系統中,也難免會出現一些帶有雙斜杠的url。tomcat相容了這種情況還是比較得民心的。

附圖-tomcat替換多個斜杠為一個斜杠

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

附圖-jetty中Violation枚舉,羅列了URI路徑中存在的各種“禁忌”,其中就有本文遇到的雙斜杠問題。

violation 英 [ˌvaɪə'leɪʃ(ə)n] 美 [ˌvaɪə'leɪʃ(ə)n] n. 違反;違法;違章;越軌;侵犯;破壞;違例;犯規
ambiguous 英 [æmˈbɪɡjuəs] 美 [æmˈbɪɡjuəs] adj. 模棱兩可的;含混不清的;不明确的      
IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)

附圖-檔案名/檔案目錄名所不允許出現的字元,其中包括斜杠字元

IDEA中用jetty啟動web項目時,url路徑包含雙斜杠會傳回404 (url裡多了一個斜杠/, jetty傳回404,tomcat正常)