研究它的原因是在用它提供的ActionBuilder
,ActionFilter
,ActionTransform
的时候,感觉API提供的不够灵活。
play将后台处理前台的请求的过程封装为Handler
,我们先只看关于http部分的。也就是只看下Action
。
下面是Action
trait的一些信息
trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result]) with Handler
trait Action[A] extends EssentialAction {
/**
* Type of the request body.
*/
type BODY_CONTENT = A
/**
* Body parser associated with this action.
*/
def parser: BodyParser[A]
/**
* Invokes this action.
*
* @param request the incoming HTTP request
* @return the result to be sent to the client
*/
def apply(request: Request[A]): Future[Result]
def apply(rh: RequestHeader): Iteratee[Array[Byte], Result]
...//下面的省略
}
Action
继承 Function1[RequestHeader,Iteratee[Array[Byte], Result]]
,也就是说,Action
其实是一个函数。这就给了Play一个天然异步的语法优势。
BodyParser
是主要处理http报体的(至于uri则存放在RequestHeader中)是业务逻辑处理函数的存放处,下面是BodyParser
继承关系
trait BodyParser[+A] extends Function1[RequestHeader, Iteratee[Array[Byte], Either[Result, A]]]
BodyParser
函数的返回结果是Either[Result,A]
,这样的做好处,是能保持业务逻辑的链式处理,可以在任何时候(尤其是throw Exception的时候)直接返回Result,不用再继续处理下去。 BodyParser
采用Monad
的编程方式,值得额外注意的是,Play为了异步添加了mapM
flatMapM
两个函数
def mapM[B](f: A => Future[B])(implicit ec: ExecutionContext): BodyParser[B]
def flatMapM[B](f: A => Future[BodyParser[B]])(implicit ec: ExecutionContext): BodyParser[B]
至于
def validate[B](f: A => Either[Result, B])(implicit ec: ExecutionContext): BodyParser[B]
def validateM[B](f: A => Future[Either[Result, B]])(implicit ec: ExecutionContext): BodyParser[B]
则是在已实现的flatMap
flatMapM
基础之上实现的。挑选validateM
看下
def validateM[B](f: A => Future[Either[Result, B]])(implicit ec: ExecutionContext): BodyParser[B] = {
// prepare execution context as body parser object may cross thread boundary
implicit val pec = ec.prepare()
new BodyParser[B] {//scala中的链式基本上是生成新实例来保持Immutable
def apply(request: RequestHeader) = self(request).flatMapM {
case Right(a) =>
// safe to execute `Done.apply` in same thread
f(a).map(Done.apply[Array[Byte], Either[Result, B]](_))(Execution.trampoline)
case left => //当Left时,直接包装成返回结果
Future.successful {
Done[Array[Byte], Either[Result, B]](left.asInstanceOf[Either[Result, B]])
}
}(pec)
override def toString = self.toString
}
}
至于Play中最为人称道的Iteratee
抽象,可以参阅这篇Blog understanding-play2-iteratees-for-normal-humans。
在我们实际运用中,Action
有一个异步和一个同步
val name1=Action{implicit request=>//同步
...
}
val name2=Action.async{implicit request=>//异步
...
}
其实同步是由异步实现的。在scala的Future中,同步变异步的方法,一般是
Future.successful(action)
Future.failed(action)
由于async
方法的写法,个人无法理解为什么这么写,故先弄个简化版本的async
,方便理解。
//简化后的`ActionBuilder`的`async`方法
def async[A](bodyParser: BodyParser[A])(block: R[A] => Future[Result]): Action[A] = new Action[A] {//类名ActionBuilder 名副其实
def parser = bodyParser
def apply(request: Request[A]) = try {
invokeBlock(request, block)
} catch {
case e: Exception => throw new Exception("....")
override def executionContext = ActionBuilder.this.executionContext
})
//实际的`async`方法
final def async[A](bodyParser: BodyParser[A])(block: R[A] => Future[Result]): Action[A] = composeAction(new Action[A] {
def parser = composeParser(bodyParser)
def apply(request: Request[A]) = try {
invokeBlock(request, block)
} catch {
// NotImplementedError is not caught by NonFatal, wrap it
case e: NotImplementedError => throw new RuntimeException(e)
// LinkageError is similarly harmless in Play Framework, since automatic reloading could easily trigger it
case e: LinkageError => throw new RuntimeException(e)
}
override def executionContext = ActionBuilder.this.executionContext
})
实际源码中async
方法拥有final
关键字,这导致我们无法override,Play团队可能基于这层考虑,爆出了
下面两个方法来作为重写补偿
protected def composeAction[A](action: Action[A]): Action[A] =action
protected def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] =bodyParser
ActionFilter
起到拦截器的作用, 通过compose来进行and
组装,但却不支持or
组装(遇到权限Filter组合时,就不能用了),这不能说不是一个遗憾,自己先写一个耍。
trait RichFilterAction[R[_]] extends ActionRefiner[R, R] { self =>
def filter[A](request: R[A]): Future[Option[Result]]
protected final def refine[A](request: R[A]): Future[Either[Result, R[A]]] =
filter(request).map(_.toLeft(request))(executionContext)
def orElse(other: RichFilterAction[R]) = new RichFilterAction[R] {
def filter[A](request: R[A]): Future[Option[Result]] = {
self.filter(request).flatMap {
case Some(result) => other.filter(request)
case None => Future.successful(None)
}(executionContext)
}
}
}
顾名思义,将输入类型转换成转化出另一种类型输出,在语义层面来说,不要用它来处理有副作用的转换。遇到有副作用的转换需求时,先交给ActionFilter过滤掉副作用,再使用ActionTransformer
来做
ActionFilter
ActionBuilder
ActionTransfer
都继承Action
。并在个各自的具体逻辑实现方法中,将其打上final
关键字,防止用户滥用invokeBlock
方法,破坏ActionFilter
,ActionBuilder
,ActionTransfer
的语义。 ActionFunction
还提供了compose,andThen方法(把它当成Function1中的同名API用)用来保证链式。