Play2 Action梳理

研究它的原因是在用它提供的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用)用来保证链式。

时间: 2024-08-30 11:44:20

Play2 Action梳理的相关文章

iframe插入视频自动播放代码

<iframe marginwidth=0 marginheight=0 src='http://www.wsview.com/yzplayerAction!play2.action?autoPlay=true&userVideoID=37481' frameborder=0 width=900 scrolling=no height=600 allowtransparency></iframe> autoPlay=true,代码是否自动播放

机器学习&amp;数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)

http://www.cnblogs.com/tornadomeet/p/3395593.html 机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理) 前言: 找工作时(IT行业),除了常见的软件开发以外,机器学习岗位也可以当作是一个选择,不少计算机方向的研究生都会接触这个,如果你的研究方向是机器学习/数据挖掘之类,且又对其非常感兴趣的话,可以考虑考虑该岗位,毕竟在机器智能没达到人类水平之前,机器学习可以作为一种重要手段,而随着科技的不断发展,相信这方面的人才需求也会越来越大.

c#知识梳理

转:http://www.cnblogs.com/zhouzhou-aspnet/articles/2591596.html 本文是一个菜鸟所写,本文面向的人群就是像我这样的小菜鸟,工作一年也辛辛苦苦学习了一年,一直没有机会梳理一下自己的知识,最近花了一些时间整理了一些C#基础知识,也算是对过去的一年做个回顾把~ 文章有点长,请自带瓜子和茶吧,请看下面C#基础知识简单架构图,不可能100%的全面,请见谅啊... 1.值类型和引用类型 1.1堆和栈 简单的说值类型存放在堆栈上面,引用类型的数据存放

struts2 知识梳理

一:struts.xml配置详解: 1.<include> 表示引入其他配置文件 2.<constant> 定义常量 3.<package>:  属性 是否必需 描述name 是 包名,作为其它包应用本包的标记extends 否 设置本包继承其它包namespace 否 设置包的命名空间,会改变url,abstact 否 设置为抽象包 4<action>和<result> <action>有name,class,method,conv

Android知识梳理之BroadcastReceiver整理

PS.不知不觉间发现自己已经做了很久很久的Android开发了,过去对知识块的梳理总是放在云笔记里面.主要的原因还是自己的笔记太杂乱,没有脉络.本着开源的精神,也趁着这段时间有空将之前云笔记里面的文章梳理下.同时将一些知识点整理出来和大家共同分享. 转载请注明出处:http://blog.csdn.net/unreliable_narrator?viewmode=contents android 的广播接收者主要分为两个方面:一个方面是广播发送者另一个方面是广播接受者.广播发送者可以自定义广播进

C#基础知识简单梳理

C#基础知识简单梳理 本文是转发博友的总结,方便自己以后随时温习: 1.值类型和引用类型 1.1堆和栈 简单的说值类型存放在堆栈上面,引用类型的数据存放在托管堆上面(它的引用地址却存放在堆栈上面)! 栈:它是一个内存数组,是一个先进后出的数据结构! 栈的特征:数据只能从栈顶进,从栈顶出! 堆:它是一个内存区域,可以分配大块区域存储某类型的数据,与栈不同的是它里面的数据可以任意排序和移除! 下面是园子的一张图,贴上来供大家参考啊! 问     题 值  类  型 引 用 类 型 这个类型分配在哪里

高屋建瓴:梳理编程约定

相关文章连接: 编程之基础:数据类型(一) 编程之基础:数据类型(二) 动力之源:代码中的“泵” 完整目录与前言 高屋建瓴:梳理编程约定 2.1 代码中的Client与Server    21 2.2 方法与线程的关系    22 2.3 调用线程与当前线程   24 2.4 阻塞方法与非阻塞方法    24 2.5 UI线程与线程    25 2.6 原子操作    26 2.7 线程安全    27 2.8 调用(Call)与回调(CallBack)    30 2.9 托管资源与非托管资源

MVC5.0知识点梳理

我们在使用MVC的时候或许总是在使用着自己一直熟悉的知识点去实现已有的功能,多梳理一些知识点让每种功能的实现方式可以多样化. 我们在开发小型系统时总是使用微软MVC的脚手架功能,比如路由可能就是使用了默认的路由,在稍微复杂或者大型的系统中其实我们可以自定义路由的. 路由约束       routes.MapRoute( name:"Language", url:"{language}/{controller}/{action}/{id}", defauts:new

play1.x vs play2.x 对比(转)

个人看到对比play1.x和play2.x比较的文章中,写的最深入,最清晰的一个.转自:http://freewind.me/blog/20120728/965.html 为了方便群中的Play初学者们,写了一篇入门引导,以帮助初学者尽快了解Play.本文之前发在另一个网站,因为觉得有些不便,还是转到博客上. 欢迎来到play的世界,在这里你将体验到与传统SSH开发网站不一样的感受.我将把我学习play的感受与经验分享给大家,希望能对大家(特别是初学者)有所帮助. Play是一个非常有创造力.让