The reason to study it is to use it to provide, ActionBuilder
ActionFilter
when, feel the ActionTransform
API is not flexible enough to provide.
Play encapsulates the process for background processing of the foreground request Handler
, and we'll look at the HTTP section first. It's just looking down Action
.
Action
Here are Action
some of trait's information
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
Inheritance Function1[RequestHeader,Iteratee[Array[Byte], Result]]
, in other words, Action
is actually a function. This gives play a natural and asynchronous grammatical advantage.
?
Bodyparser
BodyParser
is the main processing of the HTTP report body (as for the URI is stored in the Requestheader) is the business logic processing function storage place, the following is the BodyParser
inheritance relationship
BodyParser
The return result of the function is that Either[Result,A]
the benefit of this is to keep the business logic chained, and to return to result at any time (especially when the throw exception), without any further processing.
BodyParser
In Monad
the way of programming, it is worth noting that play has added mapM
flatMapM
two functions to Async
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]
As for
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]
is implemented on the basis of implementation flatMap
flatMapM
. Choose to validateM
look down
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 } }
As for the praised abstraction in play Iteratee
, you can refer to this blog Understanding-play2-iteratees-for-normal-humans.
Actionbuilder
In our actual application, Action
there is an asynchronous and a synchronous
val name1=Action{implicit request=>//同步 ...}val name2=Action.async{implicit request=>//异步 ...}
In fact, synchronization is implemented asynchronously. In the future of Scala, the method of synchronizing mutation steps is generally
Future.successful(action)Future.failed(action)
Because async
the method of writing, the individual can not understand why so write, so first get a simplified version async
, easy to understand.
Simplified ' actionbuilder ' ' async ' method def Async[a] (Bodyparser:bodyparser[a]) (block:r[a] [= Future[result]): action[a] = New Action[a] {//class name Actionbuilder Genuine def parser = Bodyparser def apply (request:request[a]) = try {Invokebloc K (Request, block)} catch {case e:exception = throw new Exception ("...") override def ExecutionContext = ActionBuilder.this.executionContext})//actual ' async ' method 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 runtimeexcept Ion (E)} override def execUtioncontext = ActionBuilder.this.executionContext})
The actual source code in the async
method has the final
keyword, which led us to override,play the team may be based on this layer of consideration, burst out
The following two methods are used as rewrite compensation
protected def composeAction[A](action: Action[A]): Action[A] =actionprotected def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] =bodyParser
Actionfilter
ActionFilter
Play the role of the Interceptor, through the compose to and
assemble, but does not support or
the assembly (when encountering the rights filter combination, it can not be used), this can not be said not a pity, I write a play first.
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
As the name implies, the input type is converted to another type of output, and at the semantic level, do not use it to deal with side-effects conversions. When faced with side effects of the conversion requirements, the first to actionfilter filter out the side effects, and then use ActionTransformer
to do
Actionfunction
ActionFilter
ActionBuilder
ActionTransfer
are inherited Action
. And in a specific logical implementation of the method, put it on the final
keyword, to prevent user abuse invokeBlock
methods, destruction ActionFilter
, ActionBuilder
ActionTransfer
the semantics.
ActionFunction
The Compose,andthen method (which is used as an API with the same name in Function1) is also provided to guarantee chaining.
Play2 Action Grooming