Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
652 views
in Technique[技术] by (71.8m points)

scala - Play: How to implement action composition

Given the followoing ActionBuilder implementations:

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 do 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 message:

/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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Function async is expecting a Future[SimpleResult], but the nested SecuredAction.async is returning an Action to the top SignedAction.async (notes that in your sample code you omit to declare requests as class and SignedAction is declared twice).

You can compose result of nested SecuredAction within SignedAction by applying it to the 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
  }
}

Such action composition can also be done without ActionBuilder (which can lead to some extra 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 be also 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) } 
    }
  }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...