Play Framework:组合多个 ActionTransformer - 我可以在多个转换器中添加字段并在 invokeBlock 中访问它们吗?

Play Framework: Composing multiple ActionTransformers - can I add fields in multiple transformers and access them all in the invokeBlock?

使用 Play 操作组合,我想知道是否有办法在多个 ActionTransformer 中向请求添加字段,以便我可以访问请求中的两个字段。

不起作用的简单示例:

import scala.concurrent.{ExecutionContext, Future}
import play.api.mvc.{Action, ActionTransformer, Request, Results, WrappedRequest}

class RequestWithName[A](request: Request[A], val name: String) extends WrappedRequest[A](request)
def addName(implicit ec: ExecutionContext) = new ActionTransformer[Request, RequestWithName] {
  override def executionContext: ExecutionContext = ec
  override def transform[A](request: Request[A]): Future[RequestWithName[A]] = ???
}

class RequestWithUserId[A](request: Request[A], val userId: String) extends WrappedRequest[A](request)
def addUserId(implicit ec: ExecutionContext) = new ActionTransformer[Request, RequestWithUserId] {
  override def executionContext: ExecutionContext = ec
  override def transform[A](request: Request[A]): Future[RequestWithUserId[A]] = ???
}

Action.andThen(addName).andThen(addUserId) { req =>
  Results.Ok(req.name + req.userId) // compile error: name not available
}

Action.andThen(addUserId).andThen(addName) { req =>
  Results.Ok(req.name + req.userId) // compile error: userId not available
}

为什么会发生这些编译错误是有道理的 - 最后一个 andThen returns 和 ActionTransformer 只有两个字段之一。但是有没有一种方法可以完成同样的事情,而不让他们意识到彼此?例如。我可以添加一个 RequestWithUserIdAndName - 但我无法将其与添加更多字段的其他转换组合在一起。

写一个像 EnrichedRequest 这样的东西怎么样,它有一个丰富的集合(作为一些密封的层次结构实现),这样你就可以先提升它,然后添加你认为合适的丰富?

sealed trait Enrichment
case class UserName(name: String) extends Enrichment
case class UserId(name: String) extends Enrichment

class EnrichedRequest[A](
  request: Request[A],
  val enrichments: List[Enrichment]
) extends WrappedRequest[A](request)
def asEnriched(implicit ec: ExecutionContext) = new ActionTransformer[Request, EnrichedRequest] {
  ...
}

def addName(implicit ec: ExecutionContext) = new ActionTransformer[EnrichedRequest, EnrichedRequest] {
  ...
}

def addUserId(implicit ec: ExecutionContext) = new ActionTransformer[EnrichedRequest, EnrichedRequest] {
  ...
}

Action.andThen(asEnriched).andThen(addName).andThen(addUserId) { ... }
Action.andThen(asEnriched).andThen(addUserId).andThen(addName) { ... }

这将很容易实现并且可以自由扩展。唯一的缺点是要提取数据,您必须执行以下操作:

enrichments.collect {
  case UserName(name) => name
}.head

提取数据。