[Playframework文檔中文翻譯] HTTP 路由

HTTP路由

(原文鏈接:http://play-framework.herokuapp.com/zh/routes 來自"Playframework中文小站 " )

 

HTTP路由(譯者注:Play的路徑映射機制)組件負責將HTTP請求交給對應的action(一個控制器Controller的公共靜態方法)處理。

對於MVC框架來說,一個HTTP請求可以看成一個事件。這個事件包含2方面的信息:

  • 請求的路徑(例如 /clients/1542, /photos/list),包括查詢字符串(Query String).
  • HTTP的請求方法 (GET, POST, PUT, DELETE)

關於REST

Representational state transfer(簡稱REST)是一種分佈式超媒體軟件架構風格,類似互聯網。

REST規定了一些關鍵的設計原則:

  • 應用的功能分散在各種資源中
  • 每個資源對應一個唯一的可訪問的資源標識符(URI)
  • 所有資源在客戶端和資源之間使用一個統一的接口來轉移狀態。

如果你使用過HTTP協議,HTTP協議的方法(譯者注:GET、POST、PUT和DELETE等)定義了這些接口。訪問資源狀態使用的協議有:

  • 客戶-服務器
  • 無狀態性
  • 緩存
  • 分層

如果應用遵循了上述的REST設計原則,那麼我們稱該應用是RESTful的。 使用Play框架很容易構建RESTful的應用:

  • Play路由通過解析URI和HTTP methods,將request請求映射到對Java方法的調用。基於正則表達式的URI模式讓你處理起來更加靈活。
  • 協議是無狀態的,這意味着你不能在服務端保存2次成功請求之間的任何狀態。
  • Play認爲HTTP是關鍵的特性,因此Play讓你可以毫無保留地訪問HTTP的所有信息。

路由文件的語法

conf/routes 文件用於配置路由規則。這個文件包含了應用的所有路徑映射。每一個路由配置項由HTTP方法,URI模式和對應的Java調用組成。

我們看看,一個路由配置項是這樣子的:

GET    /clients/{id}             Clients.show           


每一個路由配置項以一個HTTP方法開始,後面跟着URI模式,最後是Java調用的聲明。

你可以給路由文件增加註釋,以 # 開頭。

# Display a client
GET    /clients/{id}             Clients.show           


HTTP方法

HTTP方法可以是任何HTTP所支持的有效的方法:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD

此外它支持使用 WS 作爲action方法,表示一個 WebSocket 請求。

如果使用 * 作爲方法,則這個路由項將和任何HTTP請求方法相匹配。

*   /clients/{id}             Clients.show           


這個路由項將匹配以下兩者(譯者注:當然也匹配所有其他的HTTP方法):

GET /clients/1541
PUT /clients/1212


URI模式

URI模式定義了請求的路徑。請求的路徑可以定義動態URI,動態URI的部分必須包含在 {…} 中。

/clients/all


將完全匹配:

/clients/all


但是…

/clients/{id}


將同時匹配以下兩者:

/clients/12121
/clients/toto


一個URI模式可以包含一個以上的動態部分:

/clients/{id}/accounts/{accountId}


動態部分的默認匹配策略是由正則表達式 /[^/]+/ 來定義的,你也可以爲動態部分定義你自己的匹配正則表達式。

下面這個正則表達式只能接受id爲數字的URI請求:

/clients/{<[0-9]+>id}


下面這個則只接受id是一個包含4位到10位小寫字母的URI請求:

/clients/{<[a-z]{4,10}>id}


總之任何合法的正則表達式都可以在這裏使用。

注意

動態部分在此處是有命名的(譯者注:如上述例子中動態部分命名爲id)。稍候Controller控制器可以通過HTTP參數對象(Map)獲取此處的動態部分的值(譯者注:即獲取id的實際值)。

Play認爲斜槓 / 是很重要的,不可忽略。例如,看看下面這個路由項:

GET     /clients         Clients.index


它將會匹配 /clients 但是不會匹配 /clients/ 。你可以通過在斜槓 / 後加上一個問號 ? ,讓這個路由項匹配到URI尾部含有斜槓 / 或者沒有斜槓 / 的兩種情況,例如

GET     /clients/?       Clients.index


上述URI模式的尾部斜槓後面加上一個問號表示尾部斜槓是 可選的 ,除此以外,URI模式 不能 有任何其他的可選部分。

聲明Java調用

路由配置項的最後一部分是Java調用的聲明,這部分是由一個action方法的全稱來定義的,並且這個action方法必須是一個控制器Controller類中的 public static void 的方法,Controller類必須定義在 controllers 包下,而且必須作爲 play.mvc.Controller 的子類。

你可以在 controllers 包下增加自定義的包名,那樣的話你在此處的聲明就需要在Controller類名前加上自定義的包名。 controllers 包本身是固定的,所以在路由項的action聲明中不需要寫出來。

GET    /admin             admin.Dashboard.index           


指派靜態的參數

在某些情況下,你想重用一個已聲明的action,但是想定義一個特殊的路由項,這個路由項具有一些特殊的參數值。

讓我們在下面例子中看一下怎麼做:

public static void page(String id) {
    Page page = Page.findById(id);
    render(page);
}


對應的路由項是:

GET    /pages/{id}        Application.page


現在,我想爲id爲‘home’的頁面(即/pages/home)指定一個URL別名,我可以使用靜態參數定義另外一個路由項:

GET    /home              Application.page(id:'home')
GET    /pages/{id}        Application.page


當page id爲‘home’時,上面的兩個路由項是等價的。但是,由於第一個路由項的優先級比第二個路由項高,所以當page ID爲‘home’時,請求將匹配到第一個路由項。

變量和腳本

你可以在 routes 文件中用 ${ … } 的語法來使用變量,也可以用 %{ … } 的語法來使用腳本,就像在模板templates文件裏使用一樣。例如:

%{ context = play.configuration.getProperty('context', '') }%
 
# Home page
GET    ${context}         Secure.login
GET    ${context}/        Secure.login


另一個例子可以看CRUD模塊的 routes 文件,它使用 crud.types 標籤循環遍歷所有model類型,爲每一個model類型生成一個controller的路由項。

路由的優先級

很多路由項可以匹配相同的URL請求,如果有衝突的話,將按照在route文件中聲明的順序,匹配最前面的路由項。

例如:

GET    /clients/all       Clients.listAll
GET    /clients/{id}      Clients.show


對於這樣的定義,下面的URI請求:

/clients/all


將被第一個路由項攔截,並調用 Clients.listAll(儘管第二個路由項也匹配該請求)。

處理靜態資源

使用 staticDir 作爲特殊的action方法,可以將指定的文件目錄公開爲靜態資源文件的容器。

例如:

GET    /public/           staticDir:public


當請求路徑與 /public/* 匹配時,Play會從 /pubic 文件夾目錄中取得靜態資源文件。
路由優先權也適用於這種靜態資源的路由項。

反向路由:生成URL

在Java代碼中可以使用Router生成URL,所以你可以將所有URI匹配模式集中地配置在唯一的一個配置文件中,然後充滿信心地重構你的應用。
例如,下面的這個路由配置:

GET    /clients/{id}      Clients.show


在你的代碼中,可以根據Clients.show生成對應的URL:

map.put("id", 1541);
String url = Router.reverse("Clients.show", map).url;  // GET /clients/1541


由action方法反向生成URL的這個功能被集成在Play框架的很多組件中,你永遠不需要直接調用 Router.reverse 這個方法。

如果增加的參數不包含在URI匹配模式中,這些參數會被附加到請求參數(Query String)的後面。

map.put("id", 1541);
map.put("display", "full");
String url = Router.reverse("Clients.show", map).url; // GET /clients/1541?display=full


路由對象Router會按照優先級的順序找到最符合條件的路由匹配規則去生成URL。

設置 Content Types

Play根據 request.format 的值來決定HTTP響應的媒體類型 media type 。通過匹配文件後綴名, request.format 的值可以決定要使用的模板文件 template。Play 還會從 mime-types.properties 文件的映射關係中,根據媒體類型 media type 對應的 Content-type ,來決定HTTP響應的內容類型。

Play默認的響應格式是 html ,因此控制器方法 index() 默認渲染的模板文件將是 index.html 文件。通過各種方式,你可以指定一個不同的格式,這樣就可以自定義替代的模板。

在調用 render 方法之前,你可以使用編程的方式設置響應的格式。例如,爲了提供一個媒體類型 media type 爲 text/css 的層疊樣式表,你可以這樣做:

request.format = "css";  


但是,更清晰的方式是在 routes 文件中使用URL來指定格式。你可以在路由項中,給控制器的方法添加格式的聲明,以此來設置響應類型。例如,下面的路由項將處理 /index.xml 的請求,設置 xml 的響應格式並且渲染 index.xml 模板文件。

GET    /index.xml         Application.index(format:'xml')  


類似地:

GET    /stylesheets/dynamic_css   css.SiteCSS(format:'css')


對於像下面這樣的路由項,Play也可以直接從請求URL中解析出格式。

GET    /index.{format}    Application.index 


對於這個路由項, /index.xml 的請求將使用 xml 的格式並且渲染 XML 的模板文件, /index.txt 的請求將使用 txt 的格式並且渲染簡單文本(plain text)的模板文件。

Play也可以根據HTTP內容協商(content negotiation)自動選擇響應的格式。

HTTP內容協商

Play 與其他 RESTful 架構的一個共同點是,直接使用 HTTP 的功能,而不是嘗試隱藏 HTTP 或者在 HTTP 之上添加抽象層。內容協商(Content negotiation )是這樣的一個 HTTP 特性,它允許 HTTP 服務器根據不同的 HTTP 客戶端請求的媒體類型(media types),對同一個 URL ,響應不同的媒體類型( media types )。HTTP 客戶端在 Accept 請求頭部中設置媒體類型(media types),來指定接受的內容類型(content types)。例如這樣的請求表示希望得到一個 XML 的響應:

Accept: application/xml


客戶端可以指定一個以上的媒體類型(media type),也可以使用星號通配符( */ * )表示可以接受任何的媒體類型(media type):

Accept: application/xml, image/png, */*


傳統的 web 瀏覽器大多數在 Accept 頭部中包含通配符 */ * :它們將接受任何的媒體類型(media type),而 Play 會響應默認的 HTML 內容類型。內容協商(content negotiation)更常用於自定義的客戶端,例如一個期望得到 JSON 響應的 Ajax 請求,或者一個期望得到 PDF 或 EPUB 格式的文件的電子閱讀器。

在HTTP頭部中設置Content Type

如果請求的 Accept 頭部中含有 text/html , application/xhtml 或 通配符 */ * ,Play 將使用默認的格式 html 。但如果沒有通配符 */ * ,Play 將不會使用默認的格式。

Play內置了幾種支持的格式: html , txt , json and xml 。例如,定義一個控制器方法,渲染一些數據:

public static void index() { 
   final String name = "Peter Hilton"; 
   final String organisation = "Lunatech Research"; 
   final String url = "http://www.lunatech-research.com/"; 
   render(name, organisation, url); 
} 


如果瀏覽器發送的一個請求 URL 匹配了這個方法(例如使用 http://localhost:9000/ 訪問新創建的Play應用),那麼 Play 將渲染 index.html 模板文件,因爲瀏覽器請求的 Accept 頭部值含有 text/html

對於含有 Accept: text/xml 頭部的請求,Play會把響應格式設置爲 xml ,且渲染 index.xml 模板文件,例如:

<?xml version="1.0"?> 
<contact> 
<name>${name}</name> 
<organisation>${organisation}</organisation> 
<url>${url}</url> 
</contact> 


以控制器的 index() 方法爲例,Play 內置的 Accept 頭部類型映射的工作原理如下:Play將 accept 請求頭部包含的媒體類型(media type)映射到一種格式(format),從而決定渲染的模板文件。

Accept 頭部 格式(Format) 模板文件名 映射關係
null null index.html 格式爲null映射到默認的模板文件
image/png null index.html 媒體類型的文件不通過格式來映射(譯者注:通過靜態資源目錄來訪問)
*/*, image/png html index.html 格式爲html時映射到默認的媒體類型
text/html html index.html 內置的格式
application/xhtml html index.html 內置的格式
text/xml xml index.xml 內置的格式
application/xml xml index.xml 內置的格式
text/plain txt index.txt 內置的格式
text/javascript json index.json 內置的格式
application/json, */* json index.json 內置的格式, 忽略默認的媒體類型

自定義格式

通過檢查請求的頭部,並且相應地設置格式( format ),你可以自定義格式的類型。你只能設置與 HTTP 請求接受的媒體類型一致的格式。例如,在控制器中所有的請求處理之前,你可以設置自定義的格式,然後響應一個 text/x-vcard 媒體類型的 vCard

@Before 
static void setFormat() { 
	if (request.headers.get("accept").value().equals("text/x-vcard")) { 
		request.format = "vcf"; 
	} 
} 


現在,對於一個帶有 Accept: text/x-vcard 頭部的請求,Play 將渲染一個 index.vcf 模板文件,例如:

BEGIN:VCARD 
VERSION:3.0 
N:${name} 
FN:${name} 
ORG:${organisation} 
URL:${url} 
END:VCARD  


繼續討論

當路由(Router)決定對到達的 HTTP 請求執行哪一個 Java 調用之後,Play 框架將調用相應的控制器(Controller)。讓我們看看 Controllers 是如何工作的。

(原文鏈接:http://play-framework.herokuapp.com/zh/routes 來自"Playframework中文小站 " )

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章