play框架04--

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 
      路由配置总是从HTTP方法开始,URI作为中间部分,最后的元素是Java调用。在路由文件中可以使用#进行注释:
# 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请求都会被框架接受:

扫描二维码关注公众号,回复: 4315927 查看本文章
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。

2、逆向生成URL


Play路由器是按照Java调用生成URL的,所以可以将URI表达式都集中到同个配置文件中,使得重构应用变得更加便捷。比如,为conf/routes文件添加如下路由配置:

GET    /clients/{id}      Clients.show

      之后在Java代码中,就可以调用Client.show来生成URL:

map.put("id", 1541);
String url = Router.reverse("Clients.show", map).url;   //     GET /clients/1541


注意:

URL的生成已经集成到框架的大部分组件当中,一般我们不需要直接调用Router.reverse方法。


     如果增加的参数不包含在URI表达式中,这些参数会被添加到查询字符串中:

map.put("id", 1541);
map
.put("display", "full");
String url = Router.reverse("Clients.show", map).url;   //    GET /clients/1541?display=full

      同样地,路由器会根据优先顺序匹配最适的URL。


3、设置content type


  Play会根据request.format设定的值,选择指定的media类型来响应HTTP请求。该值通过文件扩展名来决定使用何种视图模板进行渲染,并且通过Play框架中的mime-types.properties文件进行映射处理(映射关系详见play\framework\src\play\libs\mime-types.properties文件),为media类型设定Content-type响应。

      Play请求的默认格式为html,因此index()控制器方法默认的渲染模板文件为index.html。如果需要指定其他的格式,有以下四种方式:


      (1)可以在程序代码调用render()方法之前进行格式设置。比如将media类型设置为text/css,就可以使用CSS文件进行渲染:

public static void index() {
    request
.format = "css";
    render
();
}


      (2)推荐一种更直接的做法,直接在routes文件中使用URL来指定格式。以下列路由配置为例:首先客户端通过index.xml请求服务器,服务器端将响应格式设置为xml,最后使用index.xml模版进行渲染。

GET   /index.xml                        Application.index(format:'xml')

      同样地,我们也可以使用CSS进行渲染:

GET   /stylesheets/dynamic_css                css.SiteCSS(format:'css')


      (3)Play还可以直接从URL中获取请求格式,动态指定渲染的模板类型。参考如下路由配置:

GET   /index.{format}                        Application.index

      当请求为/index.xml时,服务器会将返回格式设置为xml并使用相应的XMl文件进行渲染;请求为/index.txt时,则会使用文本进行渲染。


      (4)使用Play中的HTTP内容协商进行格式设置,详见以后更新的内容。


4、HTTP内容协商


 Play与其他REST架构的框架一样,直接使用HTTP方法,而不是试图隐藏HTTP或者在上面构建抽象层。内容协商是HTTP的特性,它允许HTTP服务器根据客户端的请求类型,实现同个URL提供不同的media类型响应。客户端可以在Accept header中设置media属性,指定可接收的响应类型。如果用户需要XML响应,则进行如下设置:

Accept:application/xml

      客户端可以指定多种media类型,或使用cacth-all通配符(*/*)来指定任何media类型。

Accept:application/xml,image/png,*/*
      常规的Web浏览器总是在Accept header中包含了通配符的值,这样浏览器便会接受任何media类型。Play将HTML作为默认格式进行渲染,因此在客户端使用HTTP内容协商就显得特别有用:通过Ajax请求返回JSON格式,或是使文档以PDF和EPUB形式显示等


3.1 在HTTP头中设置content type#

      如果Accept header中包含了text/html,application/xhtml或者通配符 */*,Play会选择使用其默认的请求格式(即HTML)。只有当通配符的值被显式指定时,Play才会选择其默认的请求格式。

      Play内置了一些常规格式支持:html、txt、json、xml。下例代码定义了控制器方法(Action)进行数据渲染:

public static void index() { 
   
final String name = "Peter Hilton";
   
final String organisation = "Lunatech Research";
   
final String url = "http://www.lunatech-research.com/";
   render
(name, organisation, url);
}

      如果在浏览器中访问http://localhost:9000,Play默认会使用index.html模板进行渲染,因为浏览器发送了包含text/html的Accept header。

      通过将请求的格式设置为xml,可以使用index.xml模板响应标识为Accept: text/xml的请求:

<?xml version="1.0"?> 
<contact>
<name>${name}</name>
<organisation>${organisation}</organisation>
<url>${url}</url>
</contact>

      下表针对index()控制器方法给出了Play内置的Accept header请求格式映射:Accept header包含了Play能够映射成的所有格式(最后转化为相应的模板文件),如表3.1:

(表3.1 Play内置的Accept header请求格式映射)

Accept header Format Template file name mapping
null null index.html null格式请求提供默认模版扩展
image/png null index.html media类型没有映射为指定格式
*/*, image/png html index.html 默认将media类型映射为html格式
text/html html index.html 内置映射
application/xhtml html index.html 内置映射
text/xml xml index.xml 内置映射
application/xml xml index.xml 内置映射
text/plain txt index.txt 内置映射
text/javascript json index.json 内置映射
application/json, */* json index.json 内置映射, 忽略默认media类型

3.2 自定义格式#

      在Play中可以通过检查HTTP请求头,为应用选择相应的media类型来实现自定义格式。比如使用@Before标签拦截该控制器下的所有Action,检查请求的media类型是否为text/x-vcard:

@Before 
static void setFormat() {
   
if (request.headers.get("accept").value().equals("text/x-vcard")) {
        request
.format = "vcf";
   
}
}

      如果检查后发现请求头中media类型为text/x-vcard时,将调用index.vcf模板渲染:

BEGIN:VCARD 
VERSION
:3.0
N
:${name}
FN
:${name}
ORG
:${organisation}
URL
:${url}
END:VCARD

5 、关于REST

  REST全称为Representational State Transfer,即表述性状态传输。它是一种为分布式超媒体系统(比如万维网)而设计的软件架构方式。REST定义了一些关键的规则:

  • 应用的所有功能都被划分为资源。
  • 每个资源都使用URI来唯一访问。
  • 所有资源共享统一的接口用于客户端与资源之间进行状态传输。


      如果应用使用的是HTTP协议,那么这些接口是通过一系列可用的HTTP方法来定义的。HTTP协议往往通过以下方法来使用资源的状态:

  • 客户端-服务器模式。
  • 无状态模式。
  • 缓存模式。
  • 分层模式。


      如果应用遵循了REST设计规则,那么该应用就可以被称为RESTful了。Play框架可以很容易地构建RESTful应用:

  • Play的路由器通过解析URI和HTTP方法,将请求路由至Action方法。基于正则表达形式的URI为开发提供了更好的灵活性。
  • 协议是无状态的,这意味着在两次成功的请求之间不会把任何状态保存在服务器中。
  • Play将HTTP作为关键的特性,因此框架提供了对HTTP信息的完全访问。

猜你喜欢

转载自blog.csdn.net/J_M_S_H_T/article/details/80833216