XQuery/URL 重寫基礎
您希望將簡單、簡短、直觀且設計精良的傳入 URL 對映到資料庫中的適當結構。您希望實現“酷 URL”的理想目標,並使您的 XQuery 應用程式在您的資料庫內以及其他資料庫中可移植。
eXist 中的典型 URL 格式類似於以下內容
http://www.example.com:8080/exist/rest/db/app/search.xq?q=apple
您希望使用者透過一個更酷、更少依賴於平臺的 URL(例如以下內容)訪問此頁面
http://www.example.com/search?q=apple
為了將您的 URL 轉換為後者的酷形式,您需要了解 eXist 中 URL 的基本原理。
從根本上說,eXist 的 URL 由 3 部分組成
- 主機名和埠:在上面的示例中,主機名為 www.example.com,埠為 8080
- Web 應用程式上下文:在上面的示例中,上下文為 /exist
- 路徑:在上面的示例中,路徑為 /rest/db/app/search.xq?q=apple
自定義 eXist URL 可能意味著針對 3 部分中的 1 部分或多部分。
以下某些方法使用 eXist 的 URL 重寫功能,從概念上講,該功能可以讓您的應用程式遵循 MVC(模型-檢視-控制器)設計。eXist 1.5 預先配置了體現這些原理的工作設定
- 位於 /db/myapp/ 下方的集合,透過 REST servlet 透過 /exist/rest/db/myapp/ 公開,同時可以透過 URL 重寫的方式在 /exist/apps/myapp/ 位置訪問。
- 在 /db/myapp/ 中放置一個 controller.xql 將確定此集合中的資料(即模型)如何透過 URL 重寫建立的空間呈現 - 可以說:它控制模型的檢視。
請繼續閱讀以下內容,瞭解如何在 eXist 1.4.1 版本中配置 URL 重寫以獲得相同的設定。
eXist 的預設 Web 伺服器(Jetty)的埠為 8080,它在 $EXIST_HOME/tools/jetty/etc/jetty.xml 檔案的第 51 行設定。您可以修改此檔案,也可以在啟動時透過設定 -Djetty.port=80 標誌來設定埠。
請注意,更改埠的方式因啟動 eXist 的方式而異。如果您從 bin/startup 使用 UNIX 或 DOS shell 啟動 eXist,則必須更改 startup.sh 或 startup.bat 檔案。如果您使用 UNIT tools/wrapper/exist.sh 工具或 Windows 服務自動啟動 eXist,則需要更改 jetty.xml 檔案。
重新啟動 eXist。現在,進行了此更改後,您的 URL 將變為
http://www.example.com/exist/rest/db/app/search.xq?q=apple
而不是
http://www.example.com:8080/exist/rest/db/app/search.xq?q=apple
在 Unix(包括 Mac OS X)和 Linux 上,您需要以 root 身份執行 eXist 才能繫結到埠 80。否則伺服器將無法啟動。
要將伺服器的 Web 應用程式上下文從 /exist 修剪到 /,請轉到同一個 $EXIST_HOME/tools/jetty/etc/jetty.xml 檔案的第 134 行,並將以下內容更改為
來自
<Arg>/exist</Arg>
至
<Arg>/</Arg>
重新啟動 eXist。現在,進行了此更改後,您的 URL 將變為
http://www.example.com/rest/db/app/search.xq?q=apple
而不是
http://www.example.com/exist/rest/db/app/search.xq?q=apple
在自定義 URL 的其餘部分時,eXist 的 URL 重寫功能變得既強大又具有挑戰性。(有關 eXist 中 URL 此方面的完整文件,請參閱 eXist 關於 URL 重寫的文件。)
eXist URL 重寫的核心是一個檔案,它控制其網站部分的 URL;此檔案稱為 controller.xql,您將其放置在 Web 應用程式目錄的根目錄。它控制其目錄及其子目錄中的所有 URL(儘管子目錄可以包含自己的 controller.xql 檔案 - 稍後會詳細介紹)。如果您的 Web 應用程式儲存在檔案系統上,則您可能將“controller.xql”放置在 /webapp 目錄中。如果您的 Web 應用程式儲存在 eXist 資料庫中,則您可能會將其放在 /db 集合中。在我們正在執行的示例應用程式中,您將把 controller.xql 儲存在哪裡?
當前形式:http://www.example.com/rest/db/app/search.xq?q=apple
目標 URL:http://www.example.com/search?q=apple
controller.xql 的自然位置是 /db/app 目錄,因為 search.xq 檔案(以及推測的其他 .xq 檔案)儲存在此目錄或其下方。
鑑於應用程式的根 controller.xql 的此位置,我們需要告訴 eXist 在“/db/app”目錄中查詢根 controller.xql。我們透過編輯 /webapp/WEB-INF 資料夾中的 controller-config.xml 檔案來實現。註釋掉第 27-28 行,並新增以下內容
<root pattern="/*" path="xmldb:exist:///db/app"/>
然後重新啟動 eXist。此新的根模式將把所有 URL 請求(/*)轉發到 /db/app 目錄。現在,進行了此更改後,您的 URL 將變為
http://www.example.com/search.xq?q=apple
而不是
http://www.example.com/rest/db/app/search.xq?q=apple
自定義 URL 的最後一步是建立一個 controller.xql 檔案,該檔案將接收對 /search?q=apple 的請求,並將此請求與 q 引數一起傳遞給 search.xq 檔案。
一個可以實現此目標的基本 controller.xql 檔案如下所示
xquery version "1.0";
(:~
Default controller XQuery.
Forwards '/search' to search.xq in the same directory
and passes all other requests through.
:)
(: Root path: forward to search.xq in the same collection
(or directory) as the controller.xql :)
if (starts-with($exist:path, '/search')) then
let $query := request:get-parameter("q", ())
return
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="search.xq"/>
<set-attribute name="q" value="{$query}"/>
</dispatch>
(: Let everything else pass through :)
else
<ignore xmlns="http://exist.sourceforge.net/NS/exist">
<cache-control cache="yes"/>
</ignore>
請注意,$exist:path 變數是 eXist 可供 controller.xql 檔案使用的變數。$exist:path 的值始終等於請求 URL 中控制器根目錄之後的部分。對“/search”的請求將導致 $exist:path 為“/search”。
將此查詢儲存為 controller.xql,並將其放在 /db/app 目錄中。恭喜!我們的 URL 現在已成為我們設想的非常酷的形式
http://www.example.com/search?q=apple
而不是
http://www.example.com/search.xq?q=apple
此 $exist:path 變數是 controller.xql 檔案可用的 5 個此類變數之一。(有關每個變數的更多資訊,請參閱完整的 URL 重寫文件。)這些變數可以讓您非常精細地控制請求的 URL 以及 eXist 對應用程式資源的內部路徑。
由於您可能希望根據 URL 引數(例如 q=apple)重新路由 URL 請求,您可能希望使用 request:get-parameter() 函式檢索 URL 引數,然後使用 <add-parameter> 元素顯式地將此引數傳遞給目標查詢,如示例 controller.xql 檔案中所示。
因此,在自定義 URL 的“路徑”部分時,我們實際上關注了 3 個專案
- 其根控制器目錄的根模式和路徑(回想 controller-config.xml 檔案中的 <root> 元素)
- 控制器目錄後的路徑的其餘部分
- 作為 URL 部分包含的URL 引數
此簡單示例只觸及了 URL 重寫功能的表面。使用 URL 重寫不僅可以讓您的應用程式擁有“酷 URL”,而且還可以使您的應用程式更具可移植性,無論是在您的伺服器上,還是在將您的應用程式移植到其他伺服器上。
如果您希望主應用程式位於 /db/app 中,但仍希望訪問儲存在檔案系統上的應用程式(例如管理員應用程式('/webapp/admin')),請在 controller-config.xml 中新增一個 <root> 元素,宣告您希望與檔案系統的 /webapp 目錄關聯的根模式。將您當前的根元素替換為以下內容
<root pattern="/fs" path="/"/>
<root pattern="/*" path="xmldb:exist:///db/app"/>
所有以/fs開頭的 URL 請求將被轉發到檔案系統的webapp目錄。所有其他 URL 將繼續轉發到/db/app目錄。
雖然你只需要一個controller.xql(甚至沒有)就可以正常運作,但 eXist 允許 controller.xql 檔案被放置在根控制器層次結構的任何級別,如controller-config.xml 中的 <root> 元素定義的那樣。這使得 controller.xql 檔案能夠高度針對特定目錄的關注點。eXist 會搜尋與 URL 請求的深度級別匹配的最深層次 controller.xql 檔案,向上遍歷至根控制器.xql。
確保你的條件表示式以正確的順序排列,以便規則按照該順序進行評估,並且沒有規則被無意中首先評估。換句話說,如果另一個規則匹配以 '/sea' 開頭的 URL,那麼 URL 重寫器將始終將 '/search' URL 傳遞給該規則,而不是你的 '/search' 規則。
controller.xql 中的程式碼除了通常的變數之外,還會傳遞一些其他變數。下面的 controller.xql 不進行任何轉發,而是列印其值,以及請求文件的路徑(如果存在)...
xquery version "1.0";
declare namespace exist="http://exist.sourceforge.net/NS/exist";
import module namespace text="http://exist-db.org/xquery/text";
declare variable $exist:root external;
declare variable $exist:prefix external;
declare variable $exist:controller external;
declare variable $exist:path external;
declare variable $exist:resource external;
let $document := concat($exist:root, (: $exist:prefix, :) $exist:controller, $exist:path)
return
<dummy>
<exist-root>{$exist:root}</exist-root>
<exist-prefix>{$exist:prefix}</exist-prefix>
<exist-controller>{$exist:controller}</exist-controller>
<exist-path>{$exist:path}</exist-path>
<exist-resource>{$exist:resource}</exist-resource>
<document>{$document}</document>
</dummy>
Joe Wicentowski 在 2009 年 10 月 19 日的週一,為 eXist-open 郵件列表貢獻了這篇文章的核心內容。隨後,Dan McCreary 和 Joe Wicentowski 將其編輯成現在的形式。