【问题标题】:Play Framework 2.2 action composition returning a custom objectPlay Framework 2.2 动作组合返回一个自定义对象
【发布时间】:2013-11-15 10:01:16
【问题描述】:

我正在尝试创建一个自定义 play.api.mvc.Action,它可用于根据请求填充 CustomerAccount 并将 CustomerAccount 传递给控制器​​。

documentation for Play 2.2.x 之后,我创建了ActionActionBuilder,但我似乎无法从操作中返回CustomerAccount

我当前的代码是:

case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)

case class Account[A](action: Action[A]) extends Action[A] {
  lazy val parser = action.parser

  def apply(request: Request[A]): Future[SimpleResult] = {
    AccountService.getBySubdomain(request.host).map { account => 
      // Do something to return the account like return a new AccountWrappedRequest?
      action(AccountWrappedRequest(account, request))
    } getOrElse {
      Future.successful(NotFound(views.html.account_not_found()))
    }
  }
}

object AccountAction extends ActionBuilder[AccountWrappedRequest] { 
  def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
    // Or here to pass it to the next request?
    block(request) // block(AccountWrappedRequest(account??, request))
  }

  override def composeAction[A](action: Action[A]) = Account(action) 
}

注意:这不会编译,因为 block(request) 函数需要一种我无法填充的 AccountWrappedRequest 类型。使用直接Request

时会编译

另外...

最终,我希望能够将此帐户操作与身份验证操作结合起来,以便可以将CustomerAccount 传递到身份验证操作中,并且可以根据该客户的帐户提供用户身份验证。然后我想将客户帐户和用户传递给控制器​​。

例如:

Account(Authenticated(Action))) { request => request.account; request.user ... } 或更好的是作为不需要自定义请求对象的单个对象。

【问题讨论】:

  • 我正在努力解决同样的问题,到目前为止发现以下高度相关:mariussoutier.com/blog/2013/09/17/…
  • @AndrewE 谢谢你,我以前看过那个页面,它确实有一些有用的信息。我想我想出了一个解决方案,我在下面发布了答案。希望这会有所帮助。

标签: scala playframework action before-filter playframework-2.2


【解决方案1】:

我不确定这是否是最好的方法,但我设法提出了一个似乎效果很好的解决方案。

关键是匹配请求,将其转换为invokeBlock 内的AccountWrappedRequest,然后将其传递给下一个请求。如果链中的另一个动作期望来自链中较早动作的值,则您可以类似地匹配请求,将其转换为访问请求参数所需的类型。

从原始问题更新示例:

case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)

case class Account[A](action: Action[A]) extends Action[A] {
  lazy val parser = action.parser

  def apply(request: Request[A]): Future[SimpleResult] = {
    AccountService.getBySubdomain(request.host).map { account => 
      action(AccountWrappedRequest(account, request))
    } getOrElse {
      Future.successful(NotFound(views.html.account_not_found()))
    }
  }
}

object AccountAction extends ActionBuilder[AccountWrappedRequest] { 
  def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
    request match {
      case req: AccountRequest[A] => block(req)
      case _ => Future.successful(BadRequest("400 Invalid Request"))
    }
  }

  override def composeAction[A](action: Action[A]) = Account(action) 
}

然后在另一个动作的 apply() 方法中(在我的例子中是 Authenticated 动作)你可以类似地做:

def apply(request: Request[A]): Future[SimpleResult] = {
  request match {
    case req: AccountRequest[A] => {
      // Do something that requires req.account
      val user = User(1, "New User")
      action(AuthenticatedWrappedRequest(req.account, user, request))
    }
    case _ => Future.successful(BadRequest("400 Invalid Request"))
  }
}

您可以在 ActionBuilder 中将动作链接在一起

override def composeAction[A](action: Action[A]) = Account(Authenticated(action))

如果随后将AuthenticatedWrappedRequest 传递到控制器,您将可以访问request.accountrequest.user 和所有常见的请求参数。

如您所见,在某些情况下响应未知,会生成BadRequest。实际上,据我所知,它们永远不应该被调用,但它们在那里只是以防万一。

我很想对这个解决方案有一些反馈,因为我对 Scala 还很陌生,我不确定是否有更好的方法可以得到相同的结果,但我希望这对也有人。

【讨论】:

【解决方案2】:

我写了一个独立的小(ish)示例,可以满足您的需求:

https://github.com/aellerton/play-login-example

我放弃了尝试使用游戏框架中存在的Security 类。我确信它们很好,但我就是无法理解它们。

简要指南...

在示例代码中,控制器被声明为使用AuthenticatedRequests trait:

object UserSpecificController extends Controller with AuthenticatedRequests {
  ...
}

通过RequireAuthentication 操作强制任何页面要求身份验证(或重定向以获得它):

def authenticatedIndex = RequireAuthentication { implicit request: AuthenticatedRequest[AnyContent] =>
  Ok("This content will be accessible only after logging in)
}

使用AbandonAuthentication 操作完成注销:

def signOut = AbandonAuthentication { implicit request =>
  Ok("You're logged out.").withNewSession
}

请注意,要使其正常工作,您必须覆盖 AuthenticatedRequests 特征中的方法,例如:

override def authenticationRequired[A](request: Request[A]): Future[SimpleResult] = {
  Future.successful(
    Redirect(routes.LoginController.showLoginForm).withSession("goto" -> request.path)
  )
}

还有更多;最好看代码。

HTH 安德鲁

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多