1. 程式人生 > >Play2 for Java(三:route)

Play2 for Java(三:route)

Module 3 Route路由

在這個模組中,我們將看一下Play!如何處理請求路由。我們首先來看router的作用,以及它為什麼重要。我們將看到router的基本機制是如何工作的。然後我們將研究DSL,或者說是領域特定語言,由Play!提供用來定義路由的。我們將介紹一些DSL方面的內容,比如路由定義的基本語法,通過令牌、clobbing和正則表示式定義動態URI部分,為動態部分和路由優先順序指定型別和預設值。最後,我們將簡要介紹反向路由以及Play!如何通過DSL提供型別安全路由。

3.1 Router

與幾乎所有基於mvc的web框架一樣,路由器是最基本的概念之一,但它很少被關心。路由器負責檢查傳入的HTTP請求的各個方面,正如router的名字所表達的,它的作用就是將請求路由到特定controller(控制器)的特定Action(動作)上。
Using Router:

這裡寫圖片描述

Non-Using Router

實際上,路由器是非常重要的,如果沒有它,您基本上只能使用一個操作來處理對應用程式的每個請求。

這裡寫圖片描述

在最壞的情況下,那麼這就是一個很大的If語句,在每個可能的可接受的路由中都有一個分支。然而,良好程式設計實踐的進一步應用,如單一責任原則,建議每個類都應該有一個單一的責任,最終你最終會得到一個基本的路由器。

這裡寫圖片描述

所以你可以看到路由器是一個非常重要的元件,你最終會通過應用基本的程式設計原理來建立它。幸運的是,沒有必要每次都重新建立路由器,因為每個相當完整的web框架都已經有了。

3.2 工作原理

在我們學習路由的特殊語法之前,我們來學習Play的路由元件的工作原理。

這裡寫圖片描述

如前所述,router(路由器)將傳入的HTTP請求轉換成特定的控制器的操作。它通過觀察請求的兩個關鍵方面來實現這一點;HTTP方法,或動詞,以及請求路徑。

這裡寫圖片描述

HTTP方法或動詞表示請求的意圖。HTTP方法或動詞表示請求的意圖。例如,GET請求指示從伺服器檢索資源的意圖;POST請求將表單資料傳送到伺服器,目的是建立一個新資源;PUT請求與POST類似,但在更新現有資源時通常使用它;刪除請求被用作刪除資源的意圖。這四種方法提供了實現典型的CRUD應用程式、建立、讀取、更新和刪除的基本方法。:除了典型的CRUD操作之外,還有許多其他的方法也有特定的意圖。

這裡寫圖片描述

請求路徑是URL的尾部。如果你認為URL是兩個部分;第一部分負責確保您的請求通過internet到達您的伺服器和web應用程式;第二部分由web應用程式使用,以確定要執行的操作。雖然請求的每個部分都可以應用於您的應用程式,但是router(路由器)通常只需要知道請求路徑和HTTP動詞。

3.3 路由規則

要確定請求路由到的地方,路由器需要一系列的規則。
有很多方法可以做到這一點。你可以使用基本的約定;例如,ASP.NET MVC中根據控制器名稱、動作名稱和可選的id引數的約定,提供了基本的開箱即用。這種方法對於理解該約定的開發者來說是可訪問的,並且可以提高生產力,因為它減少了需要執行的配置的配置數量,但當你的路線需要打破這個慣例時,它可能是相當有限的。或者,您可以直接一對一地對映路由規則和處理程式。Ruby的Sinatra和Node的ExpressJS都是將路由對映到處理程式的宣告方法。這是一種非常簡單、非常有效的方法來表達路線及其相關的處理程式。然而,它可能導致路由定義在您的程式碼庫周圍傳播,這使得維護變得困難。

ASP.NET MVC:

這裡寫圖片描述

Spring:

這裡寫圖片描述

註釋是提供路由的另一種方式。ASP.NET MVC和Spring都支援使用類註釋將action或handler對映到特定的路由。註釋在路由配置中提供了一定程度的靈活性,但是正如您所看到的,隨著應用程式的路由數量的增加,它們可能導致相當嘈雜的程式碼。

這裡寫圖片描述

最後,通過使用DSL來實現直接路由宣告的路由選擇(即上圖)。Rails,以及我們即將看到的Play!,兩者都使用類似DSL的方法來在單獨的檔案中指定路由。DSL方法為宣告路由定義提供了一個單點,該方法使用專門針對路由宣告的專門語法來宣告路由定義。

3.4 Play的路由

Play!在conf資料夾下的router檔案中定義您的路由。在這個檔案中,通過使用DSL來宣告,正如前面小節中提到的那樣。

這裡寫圖片描述

一條基本的路由是這樣的,路線宣告由三個主要部分組成:
    ①HTTP動詞或方法
    ②路徑或URI模式
    ③以及對Action本身的呼叫。
定義的這個路由,以簡單的語義描述的話就是:“當一個GET請求被髮送到/customer路由時,呼叫Customers控制器(注:controllers是資料夾名,而customers則是控制器類的名字,list是該控制器內的方法的名字,下面會詳細的講解)的列表操作。”

3.5 Play路由的HTTP動詞

Play!為大多數有效的HTTP謂詞(GET/POST/PUT/DELETE/TRACE等)提供支援。不過,除非你在寫一個廣泛的API,這在Play是完全可能的,除此之外您的大多數web應用程式可能會被限制在GET和POST請求上。

一個簡單的API,例如在一個頁面應用程式架構中,可能還會用到PUT和DELETE方法。

3.6 Play的路徑、URI模式

我們的路由所定義的路徑更準確地稱為URI模式,因為它可以用於捕獲多個路徑。在我們當前截圖中的那個例子中,那條router是完全固定的,這就是所謂的靜態路由.

這裡寫圖片描述

如果我們考慮一個更復雜的場景,比如/ustomer/view/2中,我們將完全不可能為資料庫中的每個客戶id定義靜態路由(見上圖)。這時,我們需要將動態部分引入到URI模式中。
在Play!中,有許多方法可以從URI模式中提取資料:
    ①它可以通過使用冒號字首標記來完成,
    ②clobbing,這是一種包羅永珍的方法
    ③更嚴格地通過帶有正則表示式驗證的符號
路由標記(即①)的方法非常常見,您可能會發現這種方法適合大多數情況,我們可以使用這種辦法來解決之前的/customer/view/2的問題。我們只需用一個:id的標記來約束id=2。當路由器匹配此路由的請求時,它將嘗試提取:id並將其解析為所需的型別,使它可以傳遞到操作呼叫。可以將任意數量的標記指定為URI模式的一部分,但標記名稱必須是惟一的,因為它們將被傳遞到下一個路由段中的操作當中去。

這裡寫圖片描述

這裡寫圖片描述

有些情況下,路由不能被嚴格定義為靜態和動態部分的集合。舉個例子,如果你想定義一個路由,從資產資料夾下面提供靜態資產,你不會想要為每個資產定義一個靜態路由,也不希望通過在資料夾和檔名中引入不同的標記來施加任何特定的結構,因為這會變得過於複雜,當然也會降低靈活性。Play!提供了clob路由引數的能力。使用一個帶有星號字首的名稱,Play!將尾隨路徑片段提取到一個字串中,並將其傳遞給action呼叫。因此,像/assets/js/appjs這樣的路由將會匹配上我們的路由,js/appjs將被傳遞給呼叫的Action。

這裡寫圖片描述

表達URI模板的動態部分的最後一種方法是正則表示式。正則表示式允許您更嚴格地指定動態段的格式。例如,在我們的客戶檢視router中,如果我們知道id始終是數字的,我們可以使用正則表示式指定id部分。那麼這有什麼區別呢?在基於標記(就是方式①)的方法中,如果我們的操作期望一個數字值,我們通過字母或非數字符號傳遞它,我們就會得到一個錯誤。所發生的是路由本身與URI模式匹配,但試圖強制將路由段轉換成數字實際上會丟擲異常。這個錯誤需要被一個自定義程式碼捕獲並顯式地處理。但是,如果我們使用正則表示式指定路由,就像我們剛才看到的那樣,路由本身不會匹配,最終會得到一個404 Not Found響應。正則表示式語法有一個潛在的缺點;如果我們更改customer的id的型別,我們不僅需要在領域模型中更改它,而且我們的領域定義已經洩漏到路由器中,我們也需要在那裡改變它。

3.7 Play路由的Action呼叫

這裡寫圖片描述

路由定義的最後一部分是對操作的實際呼叫。在Play!中,Action被定義為控制器類的靜態方法。因此,Action呼叫只不過是對Action本身的完全限定的引用。

這裡寫圖片描述

如果您的操作從路由中提取了引數,如前面所討論的,您也可以在這裡引用它們。例如,我們的檢視客戶示例可以表示為controllers.Customers.view方法,使用Long型別的id引數,從路徑中提取id引數,並轉換或解析為預期型別。

這裡寫圖片描述

還可以指定預設值。例如,如果在我們系統中有一個顯示訂單頁列表的路由,我們想要將頁碼號作為路由引數傳遞,我們可以如上圖這樣做。如果我們所提供的請求路徑時/orders/,那麼就使用預設值page=1來進行操作,就像傳來的請求時/orders/1一樣。

3.8 反向路由

可以使用路由器在Java呼叫中生成URL。這使得在單個配置檔案中集中所有URI模式成為可能,因此在重構應用程式時,您可以更加自信。

這裡寫圖片描述

// Redirect to /hello/Bob
public Result index() {
    return redirect(controllers.routes.Application.hello("Bob"));
}
對於在router檔案中使用的每個controller(控制器),路由器將在routes包中生成一個“反向控制器”,具有相同的操作方法,具有相同的簽名,但返回一個play.mvc.Call而不是play.mvc.Result。play.mvc.Call定義了一個HTTP呼叫,並提供了HTTP方法和URI。
PS:
每個控制器包都有一個routes子包。所以controllers.Application.hello操作,可以通過controllers.routes.Application.hello翻轉(只要在路由檔案中沒有其他路徑,就會與生成的路徑匹配)。
PS:
新版本應該是移除了這個工具方法~~所以就以程式碼形式呈現吧

3.9 總結

所以總而言之,Play!它的路由功能提供了一些很好的好處。集中的配置方式使router的管理變得簡單。一個乾淨的DSL使宣告簡單易讀。而反向路由特性意味著你可以得到編譯時的檢查路線和它們的引數。