內置的HTTP路由器
這個路由器用于翻譯每一個接收到的HTTP請求到對應的action(一個controller類的public方法)調用.
一個HTTP請求被視為MVC框架的一個event.這個event包含兩個主要的信息部分:
- request path (e.g. /clients/1542, /photos/list),包含query string.
- HTTP method (GET,POST,...).
路由被定義在conf/routes 文件中,是被編譯過的.這意味著你會在路由出錯的時候直接在你的瀏覽器中看到他們:
![路由出錯的時候,你會在你的瀏覽器中與她相逢](https://www.playframework.com/documentation/2.6.x/resources/manual/working/javaGuide/main/http/images/routesError.png)
路由的依賴注入
Play 支持生成兩種類型的路由器.一個是依賴注入路由器,另一個是靜態(tài)路由器.默認的是依賴注入路由器,這也是 Play的種子模板(Play seed Activator templates)中選擇的方案,因為我們推薦你使用依賴注入的controllers.如果你需要使用靜態(tài)controller,你可以切換靜態(tài)路由生成器 通過添加下面的配置到你的 build.sbt 文件中:
routesGenerator := StaticRoutesGenerator
Play 的文檔中代碼例子假定你使用的是依賴注入的路由生成器.如果是靜態(tài)路由生成器,你可以通過在路由文件中controller的調用地方使用@符號作為前綴,或者聲明每一個action方法為一個 static 方法來適配代碼示例.
路由文件的書寫語法
conf/routes 是路由器使用的配置文件.這個文件列出了應用需要的所有路由.每一個路由由一個HTTP method, 一個URI pattern和需要調用的相關action method組成.
讓我們看看路由定義的樣子:
GET /clients/:id controllers.Clients.show(id: Long)
注意:在action的調用地方,需要將參數(shù)的類型寫在參數(shù)名稱后面, 類似Scala風格.
每一個路由由HTTP method 開始,后面是URI pattern.最后是一個調用定義.你也可以添加一些注釋在路由文件中,使用 # 字符:
# Display a client.
GET /clients/:id controllers.Clients.show(id: Long)
HTTP method
這個 HTTP method 可以是任何 HTTP支持的有效方法(GET, PATCH, POST, PUT, DELETE, HEAD,OPTIONS).
URI pattern
URI pattern 定義了路由的請求路徑.請求路徑的一部分可以是動態(tài)的.
靜態(tài)的path
舉個例子,要完全匹配 GET /clients/all 這個請求,你可以這樣定義路由:
GET /clients/all controllers.Clients.list()
動態(tài)部分
如果你想定義一個路由,比如說,通過ID檢索客戶端,你需要添加一個動態(tài)部分:
GET /clients/:id controllers.Clients.show(id: Long)
注意:一個URI pattern可能有不止一個動態(tài)部分.
動態(tài)部分的默認匹配策略是由正則表達式:
[^/]+
定義的,即任何使用 :id 定義的動態(tài)部分將匹配一個URI的path段.
動態(tài)部分跨越多個/
如果你想要一個動態(tài)部分來捕獲多個URI path段胰柑,用斜線分開,你可以使用* id語法來定義一個動態(tài)部分,它使用 .* 正則表達式:
GET /files/*name controllers.Application.download(name)
這里,像 GET /files/images/logo.png 這樣的請求, 這個name動態(tài)部分將捕獲 /images/logp.png 作為值.
自定義正則表達式的動態(tài)部分
當然你通過你自己的正則表達式定義一個動態(tài)部分,使用 $id<regex> 語法:
GET /items/$id<[0-9]+> controllers.Items.show(id:Long)
調用action生成器方法
路由的最后一部分定義了一個調用.這部分必須定義一個有效的action method 調用.
如果這個調用方法沒有定義任何參數(shù)确镊,那么只需要一個方法名稱就可以:
GET / controllers.Application.homePage()
如果action方法定義了參數(shù),相對應的參數(shù)就會在request URI中查找匹配滥搭,URI中的URI path和query string都會被查找何什。
# Extract the page parameter from the path.
# i.e. http://myserver.com/index
GET /:page controllers.Application.show(page)
或者是:
# Extract the page parameter from the query string
# i.e. http://myserver.com/?page=index
GET / controllers.Application.show(page)
下面是定義在 controllers.Application控制器中對應的show方法:
public Result show(String page) {
String content = Page.getContentOf(page);
response().setContentType("text/html");
return ok(content);
}
參數(shù)類型
對于String類型的參數(shù),參數(shù)類型是可選的.如果你想要 Play去轉換參數(shù)為一個指定的Scala類型,你可以添加一個顯式類型:
GET /clients/:id controllers.Clients.show(id: Long)
然后在對應控制器的action方法參數(shù)中使用相同的類型:
public Result show(Long id) {
Client client = clientService.findById(id);
return ok(views.html.Client.show(client));
}
注意:這個參數(shù)類型的指定使用的是后綴語法.此外,泛型的指定使用[]符號代替java中的<>符號.比如,List[String]就是Java 里的List<String>.
固定值的參數(shù)
有時候你可能想要使用一個固定值的參數(shù):
# Extract the page parameter from the path, or fix the value for /
GET / controllers.Application.show(page = "home")
GET /:page controllers.Application.show(page)
具有默認值的參數(shù)
當然你也可以提供一個默認值用在一個請求沒有攜帶參數(shù)值的時候:
# Pagination links, like /clients?page=3
# 分頁的時候
GET /clients controllers.Clients.list(page: Int ?= 1)
可選參數(shù)
你可以指定只一個不需要存在于所有請求中的可選參數(shù):
# The version parameter is optional. E.g. /api/list-all?version=3.0
GET /api/list-all controllers.Api.list(version ?= null)
路由優(yōu)先級
很多路由可以匹配一些相同的請求,如果有沖突,按照聲明順序的第一個路由將被使用.
反向路由
路由器可以被java調用生成一個URL.這使你可以將你的URI patterns 集中在一個單獨的配置文件中,這樣當你重構你的應用的時候你可以更加自信.
對于路由文件使用的每一個控制器,路由器會生成一個反向控制器在routes包中,包含相同的action方法,相同的簽名但是返回的play.mvc.Result對象被paly.mvc.Call對象替代.
這個play.mvc.Call 定義了一個HTTP調用,提供相同的HTTP method和 URI.
舉個例子,如果你創(chuàng)建了一個這樣的controller:
package controllers;
import play.*;
import play.mvc.*;
public class Application extends Controller{
public Result hello(String name) {
return ok("Hello " + name + "!");
}
}
并且在 conf/routes文件中映射它:
# Hello action
GET /hello/:name controllers.Application.hello(name)
那么你可以導向一個URL到 hello action方法,通過使用 controllers.routes.Application reverse controller:
// Redirect to /hello/Bob
public Result index() {
return redirect(controllers.routes.Application.hello("Bob"));
}
注意: 這里每個controller有一個routes子包,比如 controllers.admin.Applications.hello 的action可以被反向調用通過 controllers.admin.routes.Application.hello .
高級路由
參考 Routing DSL