Play: How to implement action composition

前端 未结 3 1176
闹比i
闹比i 2020-12-02 02:10

Given the followoing ActionBuilder implementations:

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

object Si         


        
相关标签:
3条回答
  • 2020-12-02 02:58

    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) } 
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-02 03:00

    Using action-zipper you can compose ActionBuilders

    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 be executed only once :)

    0 讨论(0)
  • 2020-12-02 03:14

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

    Very simply removing Futures and async, 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  
        } } }
    
    0 讨论(0)
提交回复
热议问题