原文:https://www.playframework.com/documentation/2.5.x/ScalaRouting
內(nèi)置HTTP路由器
路由器的職責(zé)是負(fù)責(zé)轉(zhuǎn)換每一個進(jìn)入的HTTP請求給Action
一個HTTP請求可以被看做是由MVC框架的事件园匹。這個事件包含兩個主要的信息:
1.請求路徑(e.g.?/clients/1542,?/photos/list), 包含查詢字符串
2.HTTP 方法 (e.g.?GET,?POST, …).
路由是在可被編譯的conf/routes文件中被定義的嬉愧。這意味著你可以在你的瀏覽器中直接看到路由的錯誤信息
依賴注入
Play支持生成兩種路由器的類型烙博,一個是依賴注入路由器美浦,另一個是靜態(tài)路由。默認(rèn)的是依賴注入路由器,這也是在Play種子Activator模板中的樣例部念,因此我們推薦你使用依賴注入Contrlller。如果你需要使用靜態(tài)Contrller,你可以通過在你的build.sbt配置文件里添加下面這樣的配置來轉(zhuǎn)換到靜態(tài)路由生成器:
routesGenerator:= StaticRoutesGenerator
在Play的文檔中的代碼樣例假定你使用了依賴注入路由生成器型宙。如果你沒有使用這個,你可以容易的改寫代碼樣例到靜態(tài)路由生成器期吓,既可以使用@符號在路由的Controller的調(diào)用部分加上前綴早歇,或者通過聲明你的每一個Contrller為object而不是class。
路由文件的語法
conf/routes是路由器使用的配置文件讨勤。這個文件里出了應(yīng)用需要的所有的路由箭跳。每個路由由HTTP方法和URI模式組成,這兩個都與調(diào)用Action生成器相關(guān)潭千。
讓我們來看一看路由定義的樣子:
GET? /clients/:idcontrollers.Clients.show(id: Long)
每一個路由以HTTP方法開始谱姓,下來是URI模式。最后一個元素是調(diào)用定義刨晴。
你也可以以#字符開頭在路由文件中增加注釋屉来。
# Display a client.
GET ? ? /clients/:id? ? ? controllers.Clients.show(id: Long)
你可以通過一個特殊的前綴“->”,告訴路由文件使用一個不同的路由器:
->? ? ? /api? ? ? ? ? ? ? ? ? api.MyRouter
當(dāng)你和String Interpolating Routing DSL也被稱作SIRD路由整合時狈癞,或者當(dāng)你使用了多個路由文件的子項目時茄靠,這非常有用。
HTTP方法
HTTP方法可以使用任何被HTTP (GET,?PATCH,?POST,?PUT,?DELETE,?HEAD)支持的有效方法蝶桶。
URI模式
URI模式定義了路由的請求路徑慨绳,請求路徑的部分可以是靜態(tài)的。
靜態(tài)路徑
例如真竖,為了準(zhǔn)確的匹配到輸入的?GET /clients/all?請求脐雪,你可以定義這樣的路由:
GET? /clients/all? ? ? controllers.Clients.list()
動態(tài)路徑
如果你想定義一個通過ID檢索一個客戶的路由,你將需要增加一個動態(tài)的部分:
GET? /clients/:idcontrollers.Clients.show(id: Long)
注意:URI模式可能會有多個動態(tài)的部分恢共。
動態(tài)部分的默認(rèn)匹配策略是通過正則表達(dá)式?[^/]+定義的战秋,意思就是說任何一個定義為:id?的動態(tài)部分將嚴(yán)格的匹配一個URI路徑段。不像其他的模式類型讨韭,在路由中路徑段自動地URI-解碼脂信,而是在傳遞給你的Controller之前癣蟋,并在反向路由中解碼。
動態(tài)部分跨越多個/
如果你想讓動態(tài)部分可以捕獲多個通過正斜杠分割的URI路徑段狰闪,你可以使用語法*id(沒被稱為使用.*正則表達(dá)的通配符模式)定義動態(tài)部分:
GET ? ? /files/* ? namecontrollers.Application.download(name)
這個配置匹配像GET /files/images/logo.png樣的請求梢薪,動態(tài)部分name 將會捕捉到值images/logo.png?。
注意:跨越多個/的動態(tài)部分不會被路由器解碼也不會被反向路由器編碼尝哆。你的職責(zé)是驗證任何用戶可能的輸入原生的URI段。反向路由器簡單的做一個字符串的連接甜攀,因此你將需要確保結(jié)果路徑是有效的秋泄,或不是,例如规阀,含有多個前導(dǎo)斜桿或非ASCII字符恒序。
自定義正則表達(dá)的動態(tài)部分
你也可以使用$id?語法為動態(tài)部分定義你自己的動態(tài)表達(dá)式:
GET? /items/$id<[0-9]+>? ? controllers.Items.show(id: Long)
就像通配符路由一樣,參數(shù)不會被路由器編碼或反向路由器解碼谁撼。你的職責(zé)是驗證輸入歧胁,確保它在這個環(huán)境下的意義。
調(diào)用Action生成器方法
路由定義的最后一部分是調(diào)用厉碟。這部分必須定義一個有效的調(diào)用到一個返回?play.api.mvc.Action?值的方法喊巍,通常情況下是一個Controller的Action方法。
如果方法沒有定義任何參數(shù)箍鼓,僅使用完全限定的方法名:
GET? /? ? ? ? ? ? ? ? ? ? controllers.Application.homePage()
如果Action方法定義了一些參數(shù)崭参,所有這些參數(shù)的值將會在請求的URI中搜索,要么從URI路徑本身提取款咖,要么從查詢字符串中何暮。
# 從路徑中提取page參數(shù)
GET? /:page? ? ? ? ? ? controllers.Application.show(page)
或者:
# 從查詢字符串中提取頁面參數(shù)
GET ???/ ? ? controllers.Application.show(page)??
相應(yīng)的,在controllers.Application?Controller中定義show方法:
def show(page: String) = Action {
loadContentFromDatabase(page).map { htmlContent =>
Ok(htmlContent).as("text/html")
}.getOrElse(NotFound)
}
參數(shù)類型
對于String類型的參數(shù)铐殃,輸入?yún)?shù)是可以選的海洼。如果你想讓Play把傳入的參數(shù)轉(zhuǎn)換成特殊的Scala類型,你可以指定參賽類型:
GET? /clients/:idcontrollers.Clients.show(id: Long)
在?controllers.Clients?Controller中相應(yīng)的實現(xiàn)一個相同的show方法定義:
def show(id: Long) = Action {
Client.findById(id).map { client =>
Ok(views.html.Clients.display(client))
}.getOrElse(NotFound)
}
固定值的參數(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)
帶有默認(rèn)值的參數(shù)
在傳入的請求沒有找到值的時候富腊,你也可以提供一個默認(rèn)值:
# Pagination links, like /clients?page=3
GET /clients controllers.Clients.list(page: Int ?=?1)
Optional類型的參數(shù)
你也可以指定一個不需要在所有的請求中都出現(xiàn)的Optional類型的參數(shù):
# The version parameter is optional. E.g. /api/list-all?version=3.0
GET /api/list-all controllers.Api.list(version: Option[String])
路由的優(yōu)先級
許多路由可以匹配到相同的請求坏逢,如果有沖突,第一個路由(按聲明的順序)被使用蟹肘。
反轉(zhuǎn)路由
路由器也可以被用來從Scala調(diào)用內(nèi)部生成URL词疼。這讓在一個單獨的配置文件中集中所有的URI模式成為可能,因此你在重構(gòu)你的應(yīng)用是會更加自信帘腹。
對于路由文件中的每一個使用的Controller贰盗,路由器會在routes包中生成一個有相同簽名的相同Action方法的‘Reverse Controller’,但是返回play.api.mvc.Call?而不是play.api.mvc.Action.
例如阳欲,如果你創(chuàng)建一個這樣的Controller:
package controllers
import play.api._
import play.api.mvc._class
Application?extends?Controller?{ def hello(name: String) = Action { Ok("Hello "?+ name +?"!") } }
并且如果你在conf/routes文件中增加了映射:
# Hello action
GET ? ??/hello/:name? ? ?controllers.Application.hello(name)
然后你就可以使用controllers.routes.Application反轉(zhuǎn)Controller反轉(zhuǎn)URL到Action方法:
// Redirect to /hello/Bob
def helloBob = Action { Redirect(routes.Application.hello("Bob")) }
注意:每一個Controller包都有一個路由子包舵盈。因此Action controllers.admin.Application.hello?可以通過controllers.admin.routes.Application.hello(只要在路由文件中的這個路由之前沒有其他可匹配的生成路徑)被反轉(zhuǎn)陋率。
反轉(zhuǎn)Action方法的原理很簡單:它拿到你的參數(shù),并在路由模式中替換它們秽晚。在路徑段(:foo)的例子中瓦糟,值在替換完成之前解碼,對于正則模式和通配符模式赴蝇,字符串在原始形態(tài)中被替換菩浙,因此值可以跨越多個段。確保當(dāng)你把這些組件傳遞給反轉(zhuǎn)路由時忘記了這些必須的組件句伶,并避免傳遞無效的用戶輸入劲蜻。
默認(rèn)的Controller
Play有一個提供了幾個有用的Action的默認(rèn)Controller,這些可以在路由文件中直接調(diào)用:
# Redirects to https://www.playframework.com/ with 303 See Other
?GET??/about controllers.Default.redirect(to?="https://www.playframework.com/")
# Responds with 404 Not Found
?GET??/orders controllers.Default.notFound
# Responds with 500 Internal Server Error
?GET??/clients controllers.Default.error
?# Responds with 501 Not Implemented
?GET??/posts controllers.Default.todo
在這個例子中考余,GET /?重定向到外部的站點先嬉,但是它也可以重定向到其他的Action(如上面的例子中的/posts?)。
自定義路由
Play提供了一個 DSL 來定義嵌入的路由器楚堤,這個路由器被稱為?String Interpolating Routing DSL疫蔓,或者簡寫為SIRD。這個DSL有很多用途身冬,包括嵌入一個輕量級的Play服務(wù)衅胀,提供自定義或更高級的路由功能給規(guī)范的Play應(yīng)用,在測試方面甩REST服務(wù)幾條街酥筝。