为了创建Promise[Result],我们首先需要另一个promise:该promise將为我们计算实际的结果值。
val promiseOfPIValue: Promise[Double] = computePIAsynchronously() val promiseOfResult: Promise[Result] = promiseOfPIValue.map { pi => Ok("PI value computed: " + pi) }
所有的 Play 2.0 的异步调用API会返回 Promise。不管你是使用 play.api.libs.WS API调用外部web服务,还是借助Akka分配异步任务,亦或使用 play.api.libs.Akka 在actors间通信。
val promiseOfInt: Promise[Int] = Akka.future { intensiveComputation() }
def index = Action { val promiseOfInt = Akka.future { intensiveComputation() } Async { promiseOfInt.map(i => Ok("Got result: " + i)) } }
注意:Async { ... }是一个助手方法,用于从Promise[Result]中构建AsyncResult。
def index = Action { val promiseOfInt = Akka.future { intensiveComputation() } Async { promiseOfInt.orTimeout("Oops", 1000).map { eitherIntorTimeout => eitherIorTimeout.fold( timeout => InternalServerError(timeout), i => Ok("Got result: " + i) ) } } }
def index = Action { Ok("Hello World") }
然而,因为该内容是已知的,Play能够自行计算该长度并产生适当的响应头信息。
def index = Action { SimpleResult( header = ResponseHeader(200), body = Enumerator("Hello World") ) }
val file = new java.io.File("/tmp/fileToServe.pdf") val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file)
它看起来正确吗?我们仅使用enumerator指定 response body:
def index = Action { val file = new java.io.File("/tmp/fileToServe.pdf") val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file) SimpleResult( header = ResponseHeader(200), body = fileContent ) }
def index = Action { val file = new java.io.File("/tmp/fileToServe.pdf") val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file) SimpleResult( header = ResponseHeader(200, Map(CONTENT_LENGTH -> file.length.toString)), body = fileContent ) }
def index = Action { Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf")) }
def index = Action { Ok.sendFile( content = new java.io.File("/tmp/fileToServe.pdf"), fileName = _ => "termsOfService.pdf" ) }
val data = getDataStream val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(data)
我们现在可以通过ChunkedResult处理这些数据:
def index = Action { val data = getDataStream val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(data) ChunkedResult( header = ResponseHeader(200), chunks = dataContent ) }
一如既往,我们提供了便利方法完成同样工作:
def index = Action { val data = getDataStream val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(data) Ok.stream(dataContent) }
当然我们也可以用任何的Enumerator指定块数据:
def index = Action { Ok.stream( Enumerator("kiki", "foo", "bar").andThen(Enumerator.eof) ) }
Tip:Enumerator.callbackEnumerator and Enumerator.pushEnumerator convenient ways to create reactive non-blocking enumerators in an imperative style.
HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked 4 kiki 3 foo 3 bar 0
我们接收到了三块数据,最后在接到到空块后关闭该响应。
def comet = Action { val events = Enumerator( """<script>console.log('kiki')</script>""", """<script>console.log('foo')</script>""", """<script>console.log('bar')</script>""" ) Ok.stream(events >>> Enumerator.eof).as(HTML) }
如果你从浏览器中访问该action,你將会在浏览器控制台中看到三个日志记录。
import play.api.templates.Html // Transform a String message into an Html script tag val toCometMessage = Enumeratee.map[String] { data => Html("""<script>console.log('""" + data + """')</script>""") } def comet = Action { val events = Enumerator("kiki", "foo", "bar") Ok.stream(events >>> Enumerator.eof &> toCometMessage) }
提示:编写 events >>> Enumerator.eof &> toCometMessage 只是 events.andThen(Enumerator.eof).through(toCometMessage) 的另一种形式。
def comet = Action { val events = Enumerator("kiki", "foo", "bar") Ok.stream(events &> Comet(callback = "console.log")) }
def comet = Action { val events = Enumerator("kiki", "foo", "bar") Ok.stream(events &> Comet(callback = "parent.cometMessage")) }
<script type="text/javascript"> var cometMessage = function(event) { console.log('Received event: ' + event) } </script> <iframe src="/comet"></iframe>
def index = WebSocket.using[String] { request => // Log events to the console val in = Iteratee.foreach[String](println).mapDone { _ => println("Disconnected") } // Send a single 'Hello!' message val out = Enumerator("Hello!") (in, out) }
WebSocket具备检索请求头(从HTTP请求头启动一个WebSocket连接)能力,允许你取回标准头消息和session数据。然而它不具备访问 request body 和HTTP response body 的能力。
def index = WebSocket.using[String] { request => // Just consume and ignore the input val in = Iteratee.consume[String]() // Send a single 'Hello!' message and close val out = Enumerator("Hello!") >>> Enumerator.eof (in, out) }