Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
655 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 Reply

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...