Play: How to Implement Action Composition

Considering subsequent implementations ActionBuilder

:

class SignedRequest[A](request: Request[A]) extends WrappedRequest[A](request) {}

object SignedAction extends ActionBuilder[SignedRequest] {

  def invokeBlock[A](request: Request[A], block: SignedRequest[A] => Future[SimpleResult]) = {
    block(new SignedRequest(request))
  }   
}     

class SecuredRequest[A](request: Request[A]) extends WrappedRequest[A](request) {}

object SecuredRequest extends ActionBuilder[SecuredRequest] {

  def invokeBlock[A](request: Request[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
    block(new SecuredRequest(request))
  }   
}

      

How can I combine them? I've tried the following ...

object MyController extends Controller {

  def doSomething = SignedAction.async(parse.json) {
    SecuredAction.async(parse.json) { implicit request =>
      Future.successful(Ok)
  }}
}

      

... but I always get the following error:

/home/j3d/test/controllers/MyController.scala:37: type mismatch;
[error]  found   : play.api.mvc.Action[play.api.libs.json.JsValue]
[error]  required: scala.concurrent.Future[play.api.mvc.SimpleResult]
[error]       SecuredAction.async(parse.json) {
                                              ^

      

Am I missing something? Tx.

+3


source to share


3 answers


The function async

expects Future[SimpleResult]

, but invested SecuredAction.async

returns Action

to the top SignedAction.async

(note that in your example, you omit to declare the code queries like class

and SignedAction

declared twice).

You can create the result nested SecuredAction

in SignedAction

by applying it to a signed request.

package controllers

import scala.concurrent.Future

import play.api._
import play.api.mvc._

case class SignedRequest[A](request: Request[A]) 
    extends WrappedRequest[A](request) {}

object SignedAction extends ActionBuilder[SignedRequest] {

  def invokeBlock[A](request: Request[A], 
    block: SignedRequest[A] => Future[Result]) = 
    block(new SignedRequest(request))

}     

case class SecuredRequest[A](request: Request[A]) 
    extends WrappedRequest[A](request) {}

object SecuredAction extends ActionBuilder[SecuredRequest] {

  def invokeBlock[A](request: Request[A], 
    block: SecuredRequest[A] => Future[Result]) = 
    block(new SecuredRequest(request))

}

object MyController extends Controller {
  def doSomething = SignedAction.async(parse.json) { signedReq =>
    SecuredAction.async(parse.json) { implicit securedReq =>
      Future.successful(Ok)
    } apply signedReq
  }
}

      

This composition of actions can also be performed without ActionBuilder

(which can lead to some additional complexity).



package controllers

import scala.concurrent.Future

import play.api._
import play.api.mvc._

case class SignedRequest[A](request: Request[A])
case class SecuredRequest[A](request: Request[A]) 

object MyController extends Controller {
  def Signed[A](bodyParser: BodyParser[A])(signedBlock: SignedRequest[A] => Future[Result]): Action[A] = Action.async(bodyParser) { req => 
    signedBlock(SignedRequest(req)) 
  }

  def Secured[A](bodyParser: BodyParser[A])(securedBlock: SecuredRequest[A] => Future[Result]): Action[A] = Action.async(bodyParser) { req => 
    securedBlock(SecuredRequest(req)) 
  }

  def doSomething = Signed(parse.json) { signedReq =>
    Secured(parse.json) { implicit securedReq =>
      Future.successful(Ok)
    } apply signedReq.request
  }
}

      

Composition can also be done around Future[Result]

:

package controllers

import scala.concurrent.Future

import play.api._
import play.api.mvc._
import play.api.libs.json.JsValue

case class SignedRequest[A](request: Request[A])
case class SecuredRequest[A](request: Request[A]) 

object MyController extends Controller {
  def Signed[A](signedBlock: SignedRequest[A] => Future[Result])(implicit req: Request[A]): Future[Result] = signedBlock(SignedRequest(req))

  def Secured[A](signedBlock: SecuredRequest[A] => Future[Result])(implicit req: Request[A]): Future[Result] = signedBlock(SecuredRequest(req))

  def doSomething = Action.async(parse.json) { implicit req =>
    Signed[JsValue] { signedReq =>
      Secured[JsValue] { securedReq => Future.successful(Ok) } 
    }
  }
}

      

+7


source


Using action-zipper you can createActionBuilders

import jp.t2v.lab.play2.actzip._

object MyController extends Controller {

  val MyAction = SignedAction zip SecuredAction 

  def doSomething = MyAction.async(parse.json) { case (signedReq, secureReqeq) =>
    Future.successful(Ok)
  }
}

      



Json parsing will only be done once :)

+1


source


to simplify @applicius answer I think it can be done without the future, I think async / future is a separate issue.

Very simply by removing futures and asynchronous computations, we get this:

    def signed[A](signedBlock: SignedRequest[A] => Result)(implicit req: Request[A]) = signedBlock(SignedRequest(req))
    def secured[A](securedBlock: SecuredRequest[A] => Result)(implicit req: Request[A]) = securedBlock(SecuredRequest(req))

    //the use is the same as with Futures except for no async

    def doSomething = Action(parse.json) { implicit req =>
         signed[JsValue] { signedReq => secured[JsValue] { securedReq =>
         Ok  
    } } }

      

-2


source







All Articles