(本文译者是爱国者)
在Play中,路由是负责将Http请求转换为Action的组件。
在MVC框架中,一个Http请求被看成一个事件。这种事件包含两种信息:
/clients/1542
, /photos/list
, 也包括查询字符串,比如/users/?id=1001
路由匹配信息定义在conf/routes
文件中,和Play 1.x不同,该文件会被纳入编译范畴。也就是说,你可以直接在浏览器中看到路由匹配的错误。
[[images/routesError.png]]
conf/routes
文件是路由配置文件。所有的Http路由信息都在该文件定义。每条路由信息占一行,由Http请求方法,URI模式和Action
生成函数组成。下面是routes
文件的一部分内容:
GET /clients/:id controllers.Clients.show(id: Long)
这个例子定义了一个路由信息,意思是当客户端向服务器发起形如`GET /clients/123`的请求时,由控制器Clients中的Action生成函数`show`进行处理,其中请求路径上的数字`123`会赋值给`id`变量.
你可以给`route`文件添加注释,只需在每一行的开头加上`#`,比如:
# 这里是注释
# Display a client
GET /clients/:id controllers.Clients.show(id: Long)
#### Http方法
Play支持所有Http请求方法,包括`GET`,`POST`,`PUT`,`DELETE`,`HEAD`等
#### URI模式
URI模式用于匹配Http路由请求路径
##### 静态模式
假如你要准确匹配形如`GET /clients/all`这样的Http请求,你可以这样定义:
GET /clients/all controllers.Clients.list()
##### 动态模式
假如客户端希望能根据不同的客户ID查询客户信息,比`GET /clients/1`查询ID为1的客户信息,`GET /clients/2`查询ID为2的客户信息,你可以这样定义:
GET /clients/:id controllers.Clients.show(id: Long)
> 注意:你需要在`show`函数声明一个名字为`id`的参数,假如你使用其他名字,比如`clientId`,那么Play不会将`:id`的值传递过去. 此外,你还可以在URI模式上指定更多变量,比如
# 获取某个部门(根据部门ID)的男性或女性员工的信息
GET /departments/:id/gender/:gender controllers.Department.show(id: Long, String gender)
> 注意:URI模式的动态部分,如上例中的`:id`,是采用正则表达式`[^/]+`进行匹配的。也就是说,形如`/client/:id`的URI模式能匹配`/clients/123` ,但不能匹配`/clients/id/123`。
##### 如何让URI模式的动态部分匹配多个路径段?
你可以使用形如`*id`来匹配多个URI路径段,这时Play会使用`.*`正则表达式进行匹配。下面的路由会匹配形如`GET /files/images/logo.png`或`GET images/logo.png`这样的Http请求:
GET /files/*name controllers.Application.download(name)
##### 使用正则表达式
要对URI模式的动态部分实现更细粒度的匹配,就需要编写正则表达式。比如,客户的ID只有数字组成,那么可以这样定义路由规则:
GET /clients/$id<[0-9]+> controllers.Clients.show(id: Long)
其中`[0-9]+`是正则表达式。
#### 调用Action生成函数
路由定义的最后一部分是处理函数。该函数又叫做Controller Action method,它必须返回一个`play.api.mvc.Action`对象。
如果该函数没有定义任何参数,则给出函数的全路径名并以`()`结束。看下面的例子:
GET / controllers.Application.homePage()
如果action函数定义了参数,那么Play会将URI路径或查询字符串上相应的值传递给函数参数。看下面的例子:
# 将URI路径上的:page值传递给show函数的参数page
GET /:page controllers.Application.show(page)
又如:
# 如果客户端发起的请求带有查询字符串?page=XXX,则Play将XXX传递给show函数的参数page
GET / controllers.Application.show(page)
同时,我们要在`controllers.Application`控制器中定义`show`函数:
def show(page: String) = Action {
loadContentFromDatabase(page).map { htmlContent =>
Ok(htmlContent).as("text/html")
}.getOrElse(NotFound)
}
##### 参数类型
在定义路由规则时,对于字符串参数,可以省略参数的类型。如果你希望Play把参数转换成某个Scala类型,那么就需要在参数名后声明类型。 比如,客户ID都由整数组成,你可以这样定义路由:
GET /client/:id controllers.Clients.show(id: Long)
这样Play会自动将`:id`转换成`Long`类型。如果你将`: Long`省略,那么Play会把`id`的类型视为`String`. 根据上述路由的定义,你要相应修改`show`函数的定义:
def show(id: Long) = Action {
Client.findById(id).map { client =>
Ok(views.html.Clients.display(client))
}.getOrElse(NotFound)
}
##### 参数固定值
有时候你希望给controller action method的参数提供固定值。请看例子:
# Extract the page parameter from the path, or fix the value for /
GET / controllers.Application.show(page = "home")
GET /:page controllers.Application.show(page)
##### 默认参数
可以给参数提供默认值,如果无法从Http请求中解析出参数值
GET /clients controllers.Clients.list(page: Int ?= 1)
这个例子中,如果客户端发起的请求形如`GET /clients?page=3`,那么`page`的值是3;如果`GET /clients`,那么`page`的值为1
#### 路由的优先级
假如多条路由规则匹配某个Http请求,Play会按照路由规则在`route`文件中声明的顺序,匹配第一条路由规则
#### 反向路由
`route`文件定义了URL和scala函数之间的映射。你可以通过Http请求调用scala函数,也可以通过调用一个scala函数来模拟一次Http请求。Play在编译时会在`routes`包下给每个控制器(`controller`)生成反向控制器(`reverse controller`)。这些`reverse controller`含有跟`controller`中的action生成函数一样的函数(相同函数名和函数参数),只是返回`play.api.mvc.Call`对象。 一个`play.api.mvc.Call`表示一个Http调用,包含HTTP方法和URI.
比如,我在`controller.Application`定义了一个`hello`函数:
package controllers
import play.api._
import play.api.mvc._
object Application extends Controller {
def hello(name: String) = Action {
Ok("Hello " + name + "!")
}
}
然后在`conf/routes`文件中定义路由规则:
# Hello action
GET /hello/:name controllers.Application.hello(name)
当Play编译时,会在`routes`包下生成一个反向控制器`Application`. 你可以使用`routes.Application.hello("Bob")`模拟客户端发起`GET /hello/Bob`的请求
// Redirect to /hello/Bob
def helloBob = Action {
Redirect(routes.Application.hello("Bob"))
}
原文:https://github.com/playframework/Play20/wiki/ScalaRouting