需要一种在Play动作

时间:2018-04-11 04:51:57

标签: scala playframework playframework-2.6

我的所有行为都有类似的结构:

class MyController @Inject() (
  val cc: ControllerComponent)
extends AbstractController(cc) {

  def get(name: String) = Action { request =>
     try {
        commonCode(request) // Creates state
        actionSpecificCode(request)  // Uses state
     }
     catch {
       case mx: MyException =>
          mx.toResult()
       case t: Throwable =>
          // convert to BadRequest
     }
   }
}

我想将try-catch块与commonBlock()一起分解为自定义操作,以便改进的代码看起来像

class MyController @Inject() (
  val myAction: MyAction,
  val cc: ControllerComponent)
extends AbstractController(cc) {

  def get(name: String) = myAction { request =>
        actionSpecificCode(request)
   }
}

使用自定义动作合成很容易做到,例如

override def invokeBlock[A](
   request: Request[A], 
   block: (Request[A]) => Future[Result]) = { request =>
      // try block 
   }

皱纹是try块内的commonCode()需要在actionSpecificCode()内创建可访问的状态。

这样做的简单方法是将状态保存在MyAction的私有字段中,然后在控制器方法中可以访问myAction.field,但这不起作用,因为使用了相同的操作实例通过多个并发请求。

下一个想法(以及导致我迁移到实现请求属性的Play 2.6的想法)是做任何Servlet应用程序将要做的事情:在请求属性中存储请求本地状态。当我发现请求本身不可变并且向请求添加属性时,我的代码仍然保留了我无法传递动作继承链的新请求,这也是搁浅的。

(这是this original question

的后续行动

2 个答案:

答案 0 :(得分:2)

commonCode返回新请求(包含所有新状态),然后将其传递给actionSpecificCodeblock在自定义操作的上下文中将是invokeBlock参数到override def invokeBlock[A]( request: Request[A], block: (Request[A]) => Future[Result] ) = { request => try { val newRequest = commonCode(request) // Creates state block(newRequest) // Uses state } catch { ... } }

{{1}}

即使请求不可变,每个操作/过滤器都有机会将其替换为传递给链上下一个处理程序的修改版本。

答案 1 :(得分:2)

这似乎是playframework ActionRefiner的典型用法。 doc描述了如何使用它们来更改请求类型(并向其添加一些状态)。我认为,您可以使用例如ActionTransformer添加请求属性(如您所建议的那样),而不是更改请求类型。

这种方法的优势在于组成精简程序,这正是您想要实现的目标。插图是given on the same doc page,在您的情况下,这可能会变为:

class MyController @Inject() (
  val myAction: MyAction,
  val cc: ControllerComponent)
extends AbstractController(cc) {

  def commonCode = new ActionRefiner[Request, Request] {
    def refine[A](input: Request[A]) = Future.successful {
      // code taken from your other question
      val user: User = input.attrs(Attrs.User)
      input.addAttr(Attrs.User, newUser)
    }
  }

  def get(name: String) = (Action andThen commonCode) { request =>
        actionSpecificCode(request)
   }
}