码迷,mamicode.com
首页 > 其他好文 > 详细

Play2 Action梳理

时间:2015-03-28 14:31:01      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:

研究它的原因是在用它提供的ActionBuilder,ActionFilter,ActionTransform的时候,感觉API提供的不够灵活。

play将后台处理前台的请求的过程封装为Handler,我们先只看关于http部分的。也就是只看下Action

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

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

ActionBuilder

在我们实际运用中,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

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)
    }
  }
}

ActionTransformer

顾名思义,将输入类型转换成转化出另一种类型输出,在语义层面来说,不要用它来处理有副作用的转换。遇到有副作用的转换需求时,先交给ActionFilter过滤掉副作用,再使用ActionTransformer来做

ActionFunction

ActionFilter ActionBuilder ActionTransfer都继承Action。并在个各自的具体逻辑实现方法中,将其打上final关键字,防止用户滥用invokeBlock方法,破坏ActionFilter,ActionBuilder,ActionTransfer的语义。
ActionFunction还提供了compose,andThen方法(把它当成Function1中的同名API用)用来保证链式。

Play2 Action梳理

标签:

原文地址:http://my.oschina.net/myprogworld/blog/393019

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!