1、路由
Play框架中的路由器是負責將傳入的HTTP請求映射爲Action調用(即控制器中被聲明爲public static void的方法)的組件。HTTP請求被MVC框架視爲事件,其主要包括以下兩塊內容:
- 請求路徑(比如/clients/1542,/photos/list),其中可以包含查詢字符串。
- HTTP方法(GET,POST,PUT,DELETE)。
Play路由器使用的配置文件爲conf/routes,該文件列出了應用需要的所有路由規則。每條路由由HTTP方法和與Java調用相關聯的URI組成。以下是路由配置的例子:
GET /clients/{id} Clients.show
# Display a client
GET /clients/{id} Clients.show
1.1 HTTP方法#
HTTP協議支持以下所列的方法,用於指定客戶請求服務器的動作,其中GET和POST是最爲常用的兩種方法:
- GET
- POST
- PUT
- DELETE
- HEAD
Play同時也支持以WebSocket的方式來調用服務器端的Action方法
如果在路由文件中指定*作爲HTTP方法,那麼這個路由會匹配任何HTTP請求:
* /clients/{id} Clients.show
使用上述的路由配置,以下兩個HTTP請求都會被框架接受:
GET /clients/1541
PUT /clients/1212
1.2 URI表達式#
URI表達式定義了路由規則需要的請求路徑,請求路徑中允許存在動態內容,但必須被聲明在{}中。
/clients/all
/clients/all
但是如果以包含動態部分配置路由規則:
/clients/{id}
/clients/12121
/clients/toto
如果某條路由配置的URI中需要包含多個動態部分,可以採用下例方法進行配置:
/clients/{id}/accounts/{accountId}
默認情況下,動態部分的匹配策略採用的是正則表達式/[^/]+/。也可以爲動態部分定義自己的正則表達式,以下是使用正則表達式的例子。
路由規則只允許接受id爲數字的值:
/clients/{<[0-9]+>id}
路由規則確保id是長度爲4到10字符的小寫單詞:
/clients/{<[a-z]{4,10}>id}
正則表達式的使用非常靈活,還可以定義更多的路由規則,本節就不做贅述了。
動態部分指定後,控制器可以在HTTP參數map中獲取該值。
默認情況下,Play將URI尾部的斜線(“/”)作爲重要的組成部分,因爲有無“/”將會出現不同的結果。比如:
GET /clients Clients.index
該路由規則會匹配/clients,而不是/clinets/(注意這裏的區別),但可以通過在斜線後面增加問號來同時匹配兩個URI:
GET /clients/? Clients.index
URI除了尾斜線不允許有其他可選的部分。
1.3 定義Java調用#
路由定義的最後部分爲需要調用的Java方法:控制器中必須定義指定的Action方法,否則會提示找不到控制器方法的錯誤信息;必須聲明爲public static void方法;控制器需作爲play.mvc.Controller的子類定義在controllers包中。
如果控制器沒有在controllers包中定義,在配置路由規則時可以在其名稱之前增加Java包(比如admin.Dashboard.index)的說明。由於controllers包本身被Play默認包含,所以用戶在配置路由時不需要顯式地指定。
GET /admin admin.Dashboard.index
1.4 404作爲Action#
可以直接使用404作爲路由配置中的Action部分。如果這樣進行配置,對應的URL路徑就會被Play應用所忽略。比如:
# 忽略favicon請求
GET /favicon.ico 404
1.5 指定靜態參數#
在某些情況下,可能會需要基於不同的參數值定義特殊路由。以下是預先定義好的Action:
public static void page(String id) {
Page page = Page.findById(id);
render(page);
}
針對該Action,常規的路由配置爲:
GET /pages/{id} Application.page
現在給參數id=home的頁面指定一條特殊的URL,需要通過設置靜態參數來實現:
GET /home Application.page(id:'home')
GET /pages/{id} Application.page
當參數id=home時,兩條路由配置等價,但是由於前者具有較高的優先級,所以被作爲默認的URL來調用Application.page。
1.6 變量和腳本#
與模板中的使用方法類似,在routes文件中可以使用${...}作爲變量表達式,使用%{...}作爲腳本表達式,比如:
%{ context = play.configuration.getProperty('context', '') }%
# 主頁
GET ${context} Secure.login
GET ${context}/ Secure.login
在路由文件中定義變量和腳本的典型例子是CRUD模塊的routes文件。該文件中使用crud.types標籤對model類型進行迭代,爲每種類型生成控制器路由定義。以後文章會詳細介紹CRUD模塊的使用。
#{crud.types}
GET /? ${type.controllerClass.name.substring(12).replace('$','')}.index
GET /${type.controllerName} ${type.controllerClass.name.substring(12).replace('$','')}.list
GET /${type.controllerName}/new ${type.controllerClass.name.substring(12).replace('$','')}.blank
GET /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.show
GET /${type.controllerName}/{id}/{field} ${type.controllerClass.name.substring(12).replace('$','')}.attachment
GET /${type.controllerName}/{id}/edit ${type.controllerClass.name.substring(12).replace('$','')}.edit
POST /${type.controllerName} ${type.controllerClass.name.substring(12).replace('$','')}.create
POST /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.save
DELETE /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.delete
#{/crud.types}
Play會按照聲明的順序,優先選擇最先聲明的路由,比如:
GET /clients/all Clients.listAll
GET /clinets/{id} Clients.show
在上例的路由配置中,雖然請求/clients/all可以同時匹配這兩條路由配置,但按照聲明的優先順序會被第一條路由攔截,並調用相應的Clients.listAll方法。
如果id參數需要匹配5個數字,在不使用重複規則的前提下,只能連續使用五個\d元字符,而使用重複規則後,規則的如下:
GET /clinets/{<\d{5}>id} Clients.index
以下路由規則匹配2個大寫字母以及3-4個數字:
GET /clinets/{<[A-Z]{2}[0-9]{3,4}>id} Clients.index
1.7 staticDir:mapping#
Play的路由配置使用特殊的Action(staticDir)將存放靜態資源的public目錄開放。該目錄裏包含的資源可以是圖片,Javascript,Stylesheet等,這些資源將直接響應給客戶端,並不需要服務器做進一步加工處理:
GET /public/ staticDir:public
當客戶端請求/public/*路徑時,Play會從應用的public文件夾中獲取相應的靜態資源。這裏的優先級與標準路由配置一樣適用。
1.8 staticFile:mapping#
還可以直接將URL路徑映射爲靜態文件:
GET /home staticFile:/public/html/index.html
當客戶端通過GET方法請求/home時,服務器將不做任何處理直接把/public/html目錄下面的index.html文件返回給客戶端。
1.9 虛擬主機
Play的路由器具有主機匹配功能,當Action的變量需要從主機參數(指子域名,而不是子目錄)中獲取時,就顯得特別有用。比如SAAS應用可以使用如下方式配置路由規則:
GET {client}.mysoftware.com/ Application.index
根據以上配置,框架會自動獲取client的值作爲請求的參數:
public static void index(String client) {
...
}
如果在模板中使用@@{...}標籤,那麼框架會根據指定的條件來選擇對應的路由,這種方式在很多場合下都非常實用。比如,需要在產品中使用額外的服務器來提供靜態資源,則可以採用如下方式進行路由配置:
#{if play.Play.mode.isDev()}
GET /public/ staticDir:public
#{/}
#{else}
GET assets.myapp.com/ staticDir:public
#{/}
對應模板中的代碼如下:
<img src="@@{'/public/images/logo.png'}">
當應用在DEV模式下運行時,靜態資源的URL爲http://locahost:9000/public/images/logo.png;如果運行在PROD模式下,URL爲http://assets.myapp.com/images/logo.png。