技术开发 频道

高效构建Web应用 教你玩转Play框架

  控制层

  Play 框架中的控制层是模型层和视图层之间的桥梁。控制层负责接收 HTTP 请求并返回相应的响应。一般来说,控制层的典型实现是接收到 HTTP 请求之后,从请求中获取一些参数,再调用服务层对应的处理方法。服务层的方法会对领域对象进行操作,完成具体的业务逻辑。最后,某种格式的响应被返回给请求者,如 HTML 页面、JSON 数据和 XML 数据等。Play 框架的控制层实现使得完成这样的典型场景变得非常简单。

  Play 框架中的每个控制器都是一个普通的 Java 类,继承自 play.mvc.Controller 类,在包 controllers 中。控制器类中的每个公开的静态方法都表示一个动作。每个动作负责完整的请求 / 响应的流程,也就是说,所有前面提到的所有请求/响应的过程都需要在每个动作中来完成。

  参数绑定

  在控制层实现中很繁琐但是必不可少的操作就是解析 HTTP 请求中的参数。不同的 Web 开发框架会提供自己的参数解析方式。Play 框架也提供了相应的支持。Play 框架可以解析 HTTP 请求中查询字符串和 URI 路径中包含的以及请求体中以格式编码的参数。所有这些参数都放在 params 对象中,其中包含 get()、getAll() 和 put() 等方法用来获取和设置参数的值。除了这种传统的使用方式之外,Play 框架还支持直接把参数的值绑定到动作方法的参数上面。比如一个动作方法的声明是 show(String username),那么请求中的参数 username 的值会在 show() 方法被调用时作为实际参数传递进去。Play 框架会负责完成相应的类型转换。值得一提的是对于日期类型(java.util.Date)的参数,Play 框架支持多种类型的日期格式的转换。比如动作方法的声明是 display(Date postedAt),而请求的格式可能是 /display?postedAt=2010-09-22,Play 框架会自动完成相应的类型转换。

  除了常见的基本数据类型之外,Play 框架还支持直接绑定领域对象的实例。比如动作方法的声明是 create(Note note),可以在参数中直接指定对象实例的属性的值。请求的格式可能是 /create?title=Note123&content=Good。Play 框架会负责创建一个 Note 类的实例,并根据参数的值设置该实例的属性 title 和 content 的值。这种绑定方式不仅支持简单对象,还支持嵌套对象和列表。比如/create?tags[0]=ajax&tags[1]=web 可以设置列表类型属性 tags 的值。

  Play 框架的这种绑定方式还支持文件对象,使得上传文件变得非常简单。只需要在表单中添加文件上传的控件(<input type="file">)并使用 multipart/form-data编码来提交请求,在动作方法的参数中就可以获取到上传文件对应的 java.io.File 对象。比如动作方法的声明可能是 upload(File picture)。上传的文件被保存在临时目录中,在请求完成之后会被自动删除。可以在动作方法中完成对上传文件的操作。

  返回响应结果

  在控制层的动作方法完成了与业务逻辑相关的处理之后,需要把响应返回给客户端。响应的结果可能是正确完成,也可能是出现错误。Play 框架提供了方便的实现用来返回不同类型的响应。使用 play.mvc.Controller 类提供的不同方法就可以生成这些响应内容。

  请求正确完成,HTTP 状态代码为 200。使用 ok() 方法生成不带内容的响应。使用 render() 方法来生成使用模板的响应。使用 renderText() 方法生成 text/plain 类型的纯文本响应。使用 renderXml() 方法生成 text/xml 类型的 XML 格式的响应。使用 renderJSON() 方法生成 application/json 类型的 JSON 格式的响应。使用 renderBinary() 方法生成二进制内容的响应。

  跳转到新的页面,HTTP 状态代码为 3XX。使用 redirect() 方法来跳转到新的 URL。使用 notModified() 方法来返回状态代码 304。

  HTTP 状态代码 4XX。使用 unauthorized() 方法返回状态代码 401。使用 forbidden() 方法返回状态代码 403。使用notFound() 方法返回状态代码 404。

  服务器内部错误,HTTP 状态代码 5XX。使用 error() 方法返回状态代码 500。

  从上面列出的方法可以看出,Play 框架使用一些有意义的方法名称替换掉了难以记忆的 HTTP 状态代码,使用起来更加方便。同时,对于常见的响应格式,包括 HTML、XML、JSON 和二进制内容,都提供了相应的方法,使得开发人员不会遗漏掉响应中 Content-Type 的声明。

  方法拦截

  控制层的方法通常需要执行一些横切的逻辑,比如用户认证、加载通用信息和记录日志等。在 Spring 框架中,这些横切的逻辑是通过面向方面编程(AOP)的支持来实现的。Play 框架提供了更加简单易用的方法拦截支持,通过简单的标注就可以定义一些执行拦截操作的方法。这些方法必须非公开的静态方法。Play 框架支持的方法拦截标注有 @Before、@After、@Finally 和 @With 等四种。

  用 @Before 标注的方法在动作方法执行之前被调用。@After 标注的方法在动作方法执行之后被调用。@Finally 标注的方法在动作方法的响应结果已经成功生成之后被调用。这三个标注都支持额外的两个属性:priority 表示标注的方法的优先级,0 为最高;unless是一个字符串数组,表示不适用此拦截方法的动作方法的名称。如 @Before(unless="index") 表示此拦截方法不会应用在动作方法index() 上。

  如果控制器类中存在继承体系结构的话,父类中声明的拦截方法对于所有子类的动作方法都是适用的。在有些情况下,开发人员可能希望把拦截方法定义在不同的类体系结构中。由于 Java 不支持多继承,无法通过继承的方式来应用来自不同类体系结构上的拦截方法。针对这种情况,Play 框架提供了 @With 标注。在控制器类 ControllerA 中定义的拦截方法可以通过 @With 标注来应用到另外一个控制器类 ControllerB 上,而且不通过继承方式来实现。只需要在 ControllerB 中声明 @With(ControllerA.class) 即可。

  在介绍完 Play 框架的控制层之后,下面介绍视图层。

  视图层

  Web 开发框架的使用者都习惯于使用某种模板技术来生成 HTML 页面,这些技术包括常见的 JSP、ASP 和 PHP 等。Play 框架也提供了自己的模板技术,可以用来动态的创建 HTML、XML、JSON 以及其它文本类型的内容。Play 框架的模板技术使用的是 Groovy 语言。Groovy 语言的灵活性和简洁性使得 Play 框架的模板简单而且易用。在模板中可以混用静态内容和生成动态内容的各种元素。在模板中可以使用的动态元素如表3所示。


▲表3:模板中可用的动态元素

  Play 框架中的标签的作用相当于 JSP 中的标签。Play 框架本身提供一些常用的标签,开发人员也可以根据需要开发自己的标签。Play 框架内置提供的标签说明如表4所示。


▲表4:Play 框架提供的标签

  在模板中可以使用来自不同地方的变量。首先是在模板生成的时候,由控制器中的动作方法通过 renderArgs 对象来添加的。如renderArgs.put("username", "Alex") 就把一个变量 username 添加到了模板中。其次是一些隐含的变量,如 request 表示当前的 HTTP 请求,session 表示当前的会话,params 表示请求中的参数和 out 表示用来输出响应的 java.io.Writer 对象。最后就是可以通过 #{set} 来设置变量。

  模板的继承

  Play 框架中可以使用 #{extends} 和 #{doLayout} 来实现模板之间的继承。模板的继承机制对于实现灵活的页面布局很有帮助。一个模板可以定义清楚页面的基本布局结构,其它模板可以继承此模板并添加具体的内容。这样就可以避免在不同模板中重复相同的页面元素。

  在父模板中可以包含任意的内容。在需要由子模板填充的位置,使用 #{doLayout /} 进行声明即可。在子模板中通过 #{extends} 来声明所继承的模板。如 #{extends 'main.html'} 就声明继承自模板 main.html。当子模板被生成之后,将包含父模板中的内容。而子模板中只需要定义扩展的内容即可。

  自定义标签

  Play 框架自身提供的标签只能解决一些常见的需求,很多时候开发人员需要根据需要开发出自己的标签。一个标签的定义非常简单,就是一个模板文件。模板文件被存放在 app/views/tags目录下,文件的名称就是标签的名称。在标签对应的模板里面,开发人员可以添加任意的内容。标签也是支持传入参数的。在标签对应的模板文件中可以用在参数名称前面加上 _ 的方式来引用参数的值。比如一个标签在使用时的方式是 #{myTag name:'Alex' /},那么在该标签的模板文件中,就可以用 ${_name} 来引用参数 name 的值。有些标签是支持在使用的时候添加标签体的,如 #{anotherTag} 测试文字 #{/anotherTag}。对于这种情况,在标签的模板文件中可以用 #{doBody} 来引用标签体中的内容。

  在介绍完 Play 框架的视图层之后,下面介绍 HTTP 路由。

0
相关文章