籠子大了什么鳥都有。同樣的道理,不論多么細心地設計 URI 結構,在系統復雜到一定程度后,仍然難以避免路徑沖突。為此,JAX-RS 使用一些規則來定義路徑匹配的優先級。
如果某個請求路徑可以對上多個 URI 匹配模式,那么 JAX-RS 就把可能匹配上的 URI 模式先拼接完整,按照下列規則依次進行比較,直到找出最適合的匹配模式:
- 首先,字面字符數量更多的 URI 模式優先。“字面字符”就是寫死的路徑段,不包含路徑分隔符
/
和模板參數。例如 /ms/rest/movie/{id : \\d+}
包含 11 個字面字符。
- 其次,模板參數個數最多的 URI 模式優先。例如
/ms/rest/movie/{id : \\d+}
帶一個模板參數。
- 最后,含正則表達式的模板參數個數最多的 URI 模式優先。例如
/ms/rest/movie/{id : \\d+}
帶一個含正則表達式的模板參數。
現在看一個例子。回顧一下,/ms/rest/movie/{id : \\d+}
已經用來根據 ID 獲取電影信息。為了制造麻煩,現在引入 /ms/rest/movie/{title}
來根據電影標題獲取電影信息。先請你猜一猜 /ms/rest/movie/300
代表啥?ID 為 300 的神秘電影,還是我們可愛的勇士?只能跟著規則一條一條地看:
- 首先,兩個 URI 匹配模式的字面字符都是 11,下一步。
- 其次,兩個 URI 匹配模式都帶一個模板參數,下一步。
- 最后,只有
/ms/rest/movie/{id : \\d+}
帶了一個含正則表達式的模板參數,勝利!所以返回 ID 為 300 的片片。
傳說這三條規則能夠覆蓋 90% 以上的情景。不過我們馬上就能造出一個打破規則的東西:/ms/rest/movie/{title : [ \\w]+}
。經過測試,/ms/rest/movie/300
會匹配上 /ms/rest/movie/{id : \\d+}
。如何解釋?JAX-RS 規范文檔 3.7.2 定義了完整的匹配規則,對于這兩個簡單的 URI 匹配模式,似乎一直進行到底都無法比較出優先級。莫非有另外的潛規則?或者是 JAX-RS 的實現(參考實現為 Jersey)自行規定?但無論如何,搞出這種怪物本身就是一個設計錯誤,所以也不必去深究原因。