Facebook Twitter LinkedIn E-mail
magnify
formats

Play Framework Web开发教程(31): 使用View模板概述

前面介绍了MVC中的Controller 和Model部分,如果你不要Web页面(前台),那么我们的Play 教程就可以到此为止了,或者你也不想使用Play来生成HTML页面,而是使用其它Web前台技术,那么View部分你也可以不看了:-)。
不过大部分的Play应用都是需要使用Web页面的,对于HTML页面的显示,你可以使用Scala语句直接输出HTML,但是Play框架提供了更好的方法,-View模板引擎。使用View模板,你无需使用Scala语句直接生成HTML,你可以编写HTML文档,嵌入模板语言,可以大大提高工作效率:
通常在Controller中使用View模板,下图显示了Play中模板在HTTP请求-响应循环中的位置:
20140909001
使用View模板允许你重复使用部分HTML代码,比如页头,页脚,布局等。此外,使用View模板的另一个好处是可以把业务逻辑和现实逻辑分开。
Play模板引擎中使用Scala语言作为模板语言,因此支持数据类型安全,每个View模板最终都编译成Scala代码,在Controller使用View模板有如调用函数。

 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(31): 使用View模板概述已关闭评论  comments 
formats

Play Framework Web开发教程(30): 使用静态资源

从Web应用中页面上显示的不全都是需要动态生成的,通常的Web应用也包含一些静态文件:比如图像,JavaScript文件,CSS文件等。Play支持这些静态资源,方法和前面生成动态HTTP响应类型,也是通过路由配置,把HTTP请求映射到一个Controller动作。
使用缺省配置
大部分情况,你想在Web应用中使用一些静态文件,此时缺省的配置就足够了。此时,你需要把这些文件和目录放在Web应用的public目录下,而使用URL路径/assets来访问它们,后面的路径为相对于public的路径。
比如,你的Play应用使用了一个图标,存放在public/images/favicon.png, 你可以使用http://localhost:9000/assets/images/favicon.png 来访问这个图片:

<link href="/assets/images/favicon.png" rel="shortcut icon" type="image/png">

同样的,缺省你可以把javascripts文件和CSS 文件分别放在public/javascripts和public/sytlesheets目录下。

你可以查看一下conf/routes文件,缺省的HTTP路由配置,定义了如何访问静态资源:

GET /assets/*file controllers.Assets.at(path="/public", file)

这条规则,表明 HTTP GET请求 /assets/时映射到Controllers.Assets.at方法,该方法使用两个参数,告诉该action方法在什么地方查找所需文件。缺省使用public路径,如果你需要使用其它目录存放不同类型的资源,可以定义自己的路由规则,比如:

GET /images/*file controllers.Assets.at(path="/public/images", file)
GET /styles/*file controllers.Assets.at(path="/public/styles", file)

使用assets逆向路由
之前我们介绍了使用逆向路由来避免hardocded的URL,因为Assets.at也是一个普通的Action方法,因此你可以使用assets逆向路由,例如:

<link href="@routes.Assets.at("images/favicon.png")"
rel="shortcut icon" type="image/png">

缓存和ETag
除了逆向路由的优点,使用asset控制器的另外一个优点是内置的缓存支持以及和Http Entity Tag(Etag)的支持。 从而允许客户端根据需要是否要从服务器请求资源还是可以使用Cached中的文件。
比如,如果我们发送一个请求网站的图标,assets控制器计算Etag的值,然后在HTTP返回的响应消息头添加如下:
Etag: 978b71a4b1fef4051091b31e22b75321c7ff0541

ETag 的值为资源文件名和修改时间的一个Hash值。一但获得了这个ETag值,客户端,可以发出一个条件请求,表示只有在资源在上次请求后有变化时再给我,它可以在请求中添加消息头:

If-None-Match: 978b71a4b1fef4051091b31e22b75321c7ff0541

如果Play assets控制器计算出同样的Hash值,就可以通知客户端,不需要重新下载,可以使用缓存中的拷贝:

HTTP/1.1 304 Not Modified
Content-Length: 0

使用gzip 压缩资源
网页加载的速度至关重要,HTTP压缩是Web服务器和客户端解决网页大小的一个重要功能。使用gzip可以大大缩小大文本文件(比如JavaScript等)。
它的工作流程是:首先浏览器(客户端)告诉服务器,它可以接受压缩过的资源,这可以通过在请求消息头中添加:
Accept-Encoding:gzip
来告知服务器,它支持的压缩方法,服务器由此可以发送一个压缩过的资源,而不是原来未经处理的资源。同时告诉浏览器压缩的方法:
Content-Encoding: gzip
对于Play应用,这部分处理是内置的,不需要程序员做特别处理,如果浏览器支持压缩,Play应用自动压缩资源然后发给浏览器。这种情况发生在下面条件为真时:

  • Play运行在产品模式(Prod 模式),在开发环境中,压缩不是期望的结果。
  • Play接受的请求被映射到Asset控制器。
  • HTTP请求消息头包含有Accept-Encoding: gzip
  • HTTP请求映射到静态文件,存在同名的含有.gz后缀的文件

如果其中任意一个条件为假,Asset控制器就发回原始(非压缩)文件。

后面我们跳过MVC中的Model部分,这部分可以参考Slick开发教程,和Play开发本身关系不是非常密切。你也可以选用其它ORM框架,或者直接使用SQL。

 

 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(30): 使用静态资源已关闭评论  comments 
formats

Play Framework Web开发教程(29): HTTP响应的消息头

除了上篇介绍的HTTP响应的状态码之外,Play也允许你定制响应的消息头,也就是指导客户端(比如浏览器)如何处理响应消息的元数据。比如对于之前的501 消息,我们可以加上一个消息的长度为0,表示没有消息体:
HTTP/1.1 501 Not Implemented
Content-Length: 0

再比如重定向消息 ,302 ,可以添加Location消息头,告诉客户端新的网站来取得所需的资源:

HTTP/1.1 302 Found
Location: http://localhost:9000/products

实际上,Play内部实现Redirect方法,就是为HTTP响应添加Location消息头:

Status(FOUND).withHeaders(LOCATION -> url)

你可以使用类似的方法来定制你的HTTP响应的消息。比如你实现了一个Web Service来添加一个产品,你可以返回一个201 消息,通知客户端创建产品成功,同时添加一个Location消息头,给出新创建产品的URL:

HTTP/1.1 201 Created
Location: /product/5010255079763
Content-Length: 0

你可以使用如下代码,构成上面的响应消息:

val url = routes.Products.details(product.ean).url
Created.withHeaders(LOCATION -> url)

其中 routes.Products.details(product.ean).url 为逆向路由。

设置Content Type
每个包含HTTP消息体的HTTP响应一般都包含一个Content-Type消息头,它的类型为一MIME类型,表示消息体的数据格式。Play自动为一些已知的消息类型添加个Content-Type消息头 ,比如HTML页面text/html,而对于字符串使用 text/plain。
如果你需要返回JSON类型的数据,你可以重新设置缺省的text/plain类型:

val json = """{ "status": "success" }"""
Ok(json).withHeaders(CONTENT_TYPE -> "application/json")

这是一个非常常见的用法,因此Play提供了一些便利的辅助函数来实现同样的功能:

Ok("""{ "status": "success" }""").as("application/json")

这还可以简化使用JSON 常量(定义在play.api.http.ContentTypes,Controller派生于这个trait)

Ok("""{ "status": "success" }""").as(JSON)

Session数据
有些时候,你需要保存一些数据来记住用户正在进行的操作,比如你需要记住用户查询的关键字,以便用户重复查询。此时一种解决方案是使用Session数据,它是由个Key-Value(键-值)对,它对于当前用户的回话有效,也就是在用户关闭浏览器前有效, 下面是你使用Session数据的基本方法:
首先,是在一个Session中保存要保存的数据:

Ok(results).withSession(
	request.session + ("search.previous" -> query)
)

然后在其它需要使用”search.previous”的地方读取它的值:

val search = request.session.get("search.previous")

如果需要删除Session中某个值,可以使用如下代码:

Ok(results).withSession(
	request.session - "search.previous"
)

注意:Session 的实现是使用HTTP Cookie来实现,因此它的总的容量是几个K字节。所以一般用来保存少量数据。 为安全起见,Play的Cookie 数据是使用密匙加密的,密匙定义在conf/application中,随机生成。

flash数据
这里的Flash不是Flash 动画的Flash,可以理解成闪存,临时保存数据的地方,它的用法和Session数据类型,所不同是,它只在下一次请求有效,然后自动删除:
比如我们使用如下代码保存一个Flash数据:

Redirect(routes.Products.flash()).flashing(
	"info" -> "Product deleted!"
)

在下一页,使用这个数据:

val message = request.flash("info")

Session和Flash数据的实现都是使用HTTP Cookie,Play也提供了直接使用Cookie的API,不过通常情况下还是要避免直接使用Cookie。

 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(29): HTTP响应的消息头已关闭评论  comments 
formats

Play Framework Web开发教程(28): HTTP状态码

最简单的HTTP响应只需要一行状态码来描述处理请求的结果,比如:
HTTP/1.1 501 Not Implemented
我们看看在Play中如何生成所需的状态码。
对于这个Not Implemented 状态码, 可以通过如下代码实现:

def list = Action { request =>
	NotImplemented
}

我们前面提到一个Action方法都是一个函数(Request=>Result)由HTTP请求到HTTP响应的映射。
这个NotImplemented 作为一个play.api.mvc.Status,代表501 ,Status为play.api.mvc.Result的子类,也就是说前面的代码可以下面的等价:

def list = Action {
	new Status(501)
}

NotImplemented 是定义在play.api.mvc.Controller 多个状态码的其中之一。

 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(28): HTTP状态码已关闭评论  comments 
formats

Play Framework Web开发教程(27): 生成HTTP响应(二)

二进制数据
通常情况下,Web应用使用的二进制数据都是静态数据文件,比如图像,关于使用这些静态文件我们后面再说,这里我们介绍动态生成的二进制文件(比如PDF文件,图像等),对于Play应用来说,返回二进制文件和前面提到的文本文件,JSON数据等没有太大的区别,所不同的是,你需要设置正确的内容类型(Content Type)。
比如,前面前面例子显示条码,我们使用的是开源条码函数库barcode4j(http://sourceforge.net/projects/barcode4j/),如果在项目中需要引用这个库,需要在build.sbt中添加这个库的引用:

libraryDependencies ++= Seq(
  jdbc,
  anorm,
  cache,
  "net.sf.barcode4j" % "barcode4j" % "2.0"
)     

之后,我们就可以定义Barcode控制器,定义barcode方法

package controllers
import play.api.mvc.{Action, Controller}
object Barcodes extends Controller {
  val ImageResolution = 144
  def barcode(ean: Long) = Action {
    val MimeType = "image/png"
    try {
      val imageData = ean13BarCode(ean, MimeType)
      Ok(imageData).as(MimeType)
    }
    catch {
      case e: IllegalArgumentException =>
        BadRequest("Couldn’t generate bar code. Error: " + e.getMessage)
    }
  }
  def ean13BarCode(ean: Long, mimeType: String): Array[Byte] = {
    import java.io.ByteArrayOutputStream
    import java.awt.image.BufferedImage
    import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider
    import org.krysalis.barcode4j.impl.upcean.EAN13Bean
    val output: ByteArrayOutputStream = new ByteArrayOutputStream
    val canvas: BitmapCanvasProvider =
      new BitmapCanvasProvider(output, mimeType, ImageResolution,
        BufferedImage.TYPE_BYTE_BINARY, false, 0)
    val barcode = new EAN13Bean()
    barcode.generateBarcode(canvas, String valueOf ean)
    canvas.finish
    output.toByteArray
  }
}

注意其中mimetype设为 image/png

然后我们可以定义路由配置

GET         /barcode/:ean         controllers.Barcodes.barcode(ean: Long)

运行后,我们就可以显示条码

20140902001

 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(27): 生成HTTP响应(二)已关闭评论  comments 
formats

Play Framework Web开发教程(26): 生成HTTP响应(一)

之前介绍了很多关于HTTP请求的文章,但我们还没说如何处理这些HTTP请求,本篇和后面几篇介绍如果生成HTTP响应,发送回客户端(比如浏览器)。
一个HTTP响应包括:一个HTTP响应状态码,然后是可选的响应头和响应消息体。 Play支持你控制这三个部分的构成,由此你可以生成任意类型的HTTP响应,同时Play提供了很多API以帮助你生成HTTP响应。
你可以多种工具来帮助你调试HTTP响应,使用这些工具你可以检查原始的HTTP消息头,消息体,很多Web浏览器也提供了插件,一个比较常用的工具是Fiddler.

之前的例子,我们请求/products ,Web应用处理这个请求,返回产品列表的表现形式representation,显示在页面上,这个Web响应的消息体包含了产品列表的信息,以某种数据格式返回,在实际开发中,下面几种为常见的数据返回格式:

  • 普通文本 –比如错误消息,或者是轻量级的Web服务。
  • HTML –一个Web页面,除了返回资源的数据表现还包含了用户界面元素
  • JSON –一个非常流行的XML替代数据格式,通常用作Ajax应用中。
  • XML –通常用在Web服务
  • 二进制数据 –通常为非文本媒体数据,比如图像和声音等

 

返回文本
为了在Action方法中输出文本,通常是使用一些预定义的方法(比如OK方法),通过传入字符串类型参数实现,比如:

def version = Action {
	Ok("Version 2.0")
}

这个例子返回一个文本字符串“Version 2.0”。

返回HTML
最常见的Web响应是Web页面,也就是HTML文档,原则上一个Web页面也是个文本(字符串),但在实现上通常我们使用一个模板系统帮助生成页面,关于模板,我们后面再详细介绍,现在你只需要知道Play将显示模板编译后放在View包中,这些编译后的模板函数返回HTML类型的数据。为了显示一个页面模板,你可以使用类似返回文本的方法:例如:

def index = Action {
	Ok(views.html.index())
}

返回JSON代码
你可以使用两种方法来返回JSON数据,你可以使用JSON模板,它的使用和使用HTML模板类似。或者你使用一些辅助函数通过序列化Scala对象来生成,比如:
比如你需要实现一个Web服务,返回一个JSON { “status”: “success” } 响应,最简单的方法是序列化(串行化)Scala Map对象:

def json = Action {
	import play.api.libs.json.Json
	val success = Map("status" -> "success")
	val json = Json.toJson(success)
	Ok(json)
}

返回XML
返回XML数据和返回JSON数据类似,序列化Scala对象到XML,或者使用XML模板。另外还有一种简单的方法,是使用scala.xml.NodeSeq字面量,比如你可以直接传入一个XML字面量作为返回结果:

def xml = Action {
	Ok(<status>success</status>)
}
 
Tags: ,
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
Play Framework Web开发教程(26): 生成HTTP响应(一)已关闭评论  comments