5

我正在尝试理解 Play 2.0 框架的反应式 I/O 概念。为了从一开始就获得更好的理解,我决定跳过框架的帮助程序来构建不同类型的迭代器,并Iteratee从头开始编写一个自定义以供 aBodyParser用于解析请求正文。

从IterateesScalaBodyParser文档中提供的信息以及关于播放反应式 I/O 的两个演示文稿开始,这就是我想出的:

import play.api.mvc._
import play.api.mvc.Results._
import play.api.libs.iteratee.{Iteratee, Input}
import play.api.libs.concurrent.Promise
import play.api.libs.iteratee.Input.{El, EOF, Empty}

01 object Upload extends Controller {
02   def send = Action(BodyParser(rh => new SomeIteratee)) { request =>
03     Ok("Done")
04   }
05 }
06
07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] {
08   println(state + " " + input + " " + received)
09
10   def fold[B](
11     done: (Either[Result, Int], Input[Array[Byte]]) => Promise[B],
12     cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B],
13     error: (String, Input[Array[Byte]]) => Promise[B]
14   ): Promise[B] = state match {
15     case 'Done => { println("Done"); done(Right(received), Input.Empty) }
16     case 'Cont => cont(in => in match {
17       case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length)
18       case Empty => copy(input = in)
19       case EOF => copy(state = 'Done, input = in)
20       case _ => copy(state = 'Error, input = in)
21     })
22     case _ => { println("Error"); error("Some error.", input) }
23   }
24 }

(备注:所有这些东西对我来说都是新的,所以如果这完全是废话,请原谅。) Iteratee 非常愚蠢,它只是读取所有块,总结接收到的字节数并打印出一些消息。当我用一些数据调用控制器操作时,一切都按预期工作 - 我可以观察到 Iteratee 接收到所有块,当读取所有数据时,它切换到状态完成并且请求结束。

现在我开始使用代码,因为我想看看这两种情况的行为:

  • 在读取所有输入之前切换到状态错误。
  • 在读取所有输入之前切换到完成状态并返回 aResult而不是Int.

我对上述文档的理解是两者都应该是可能的,但实际上我无法理解观察到的行为。为了测试第一种情况,我将上述代码的第 17 行更改为:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)

所以我只是添加了一个条件,如果收到超过 10000 个字节,则切换到错误状态。我得到的输出是这样的:

'Cont Empty 0
'Cont El([B@38ecece6) 8192
'Error El([B@4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error

然后请求永远挂起并且永远不会结束。我对上述文档的期望是,当我在Iterateeerror内部调用函数时fold,应该停止处理。这里发生的是 Iteratee 的 fold 方法在被调用后被调用了几次error——好吧,然后请求挂起。

当我在读取所有输入之前切换到完成状态时,行为非常相似。将第 15 行更改为:

15    case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }

和第 17 行到:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)

产生以下输出:

'Cont Empty 0
'Cont El([B@16ce00a8) 8192
'Done El([B@2e8d214a) 16384
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)

再次请求永远挂起。

我的主要问题是为什么请求在上述情况下挂起。如果有人能阐明这一点,我将不胜感激!

4

2 回答 2

4

您的理解是完全正确的,我只是将修复推送到大师:

https://github.com/playframework/Play20/commit/ef70e641d9114ff8225332bf18b4dd995bd39bcc

修复了 Iteratees 中的两种情况以及异常。

在案例类中很好地使用副本来做一个 Iteratee BTW。

于 2012-07-14T22:15:39.507 回答
0

Play 2.1 一定发生了变化——Promise 不再是参数化的,这个例子不再编译。

于 2013-02-22T05:37:48.603 回答