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。