Цепная игра ActionRefiners дает ActionBuilder [WrappedRequest, AnyContent] вместо ActionBuilder [DerivedClass, AnyContent] - PullRequest
0 голосов
/ 02 апреля 2020

Чтобы добавить новый параметр в большое количество существующих конечных точек, я создал новый ActionRefiner, который будет обрабатывать новый параметр и добавлять информацию для параметра в существующий запрос. Однако, когда я добавляю новый ActionRefiner в цепочку ActionRefiners в конечной точке, в результате получается ActionBuilder[WrappedRequest, AnyContent], что не позволяет мне получить доступ к данным, добавленным к запросам ActionRefiners. Например,

import scala.concurrent.{ExecutionContext, Future}

import com.google.inject.Inject
import play.api.mvc._

// Base requests.
class IntRequest[A](val num: Int, request: Request[A]) extends WrappedRequest[A](request)
class CharRequest[A](val char: Char, request: Request[A]) extends WrappedRequest[A](request)
// Actions for creating base requests.
class IntAction @Inject()(val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionBuilder[IntRequest, AnyContent]
    with ActionTransformer[Request, IntRequest] {

  override def transform[A](request: Request[A]): Future[IntRequest[A]] = {
    Future.successful(new IntRequest[A](1, request))
  }
}
class CharAction @Inject()(val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionBuilder[CharRequest, AnyContent]
    with ActionTransformer[Request, CharRequest] {

  override def transform[A](request: Request[A]): Future[CharRequest[A]] = {
    Future.successful(new CharRequest[A]('a', request))
  }
}

// Request that contains a string and the previous request.
class StringRequest[A, PrevReqT <: Request[A]](val str: String, val prevReq: PrevReqT) extends WrappedRequest[A](prevReq)
// Enclosing class to hold the type parameter for the previous request.
// Simmilar to https://stackoverflow.com/questions/38988062/in-scala-is-it-possible-to-curry-type-parameters-of-a-def
class ChainAction[PrevReqT[X] <: Request[X]] {
  // Type to ensure that the response has one type paramter in order to conform to the expectations of ActionRefiner.
  type ChainedReqT[A] = StringRequest[A, PrevReqT[A]]

  // Get the actual ActionRefiner for augmenting the previous request.
  def chainedAction(str: String): ActionRefiner[PrevReqT, ChainedReqT] = {
    new ActionRefiner[PrevReqT, ChainedReqT] {
      override def executionContext: ExecutionContext = scala.concurrent.ExecutionContext.global

      override def refine[A](request: PrevReqT[A]): Future[Either[Result, ChainedReqT[A]]] = {
        Future.successful(Right(new ChainedReqT[A](str = str, prevReq = request)))
      }
    }
  }
}

object ChainAction { def get[PrevReqT[X] <: Request[X]](): ChainAction[PrevReqT] = new ChainAction[PrevReqT] }

@Inject class Controller(
  intAction: IntAction,
  charAction: CharAction
) {
  def intEndpoint(): Action[AnyContent] = {
    intAction
      .andThen(ChainAction.get[IntRequest].chainedAction("int value is "))
      // Async should take a StringRequest => Result, but instead it takes a WrappedRequest => Result.
      .async { request: StringRequest[AnyContent, IntRequest[AnyContent]] =>
        Future.successful(Results.Ok(request.str + request.prevReq.num))
      }
  }

  def charEndpoint(): Action[AnyContent] = {
    charAction
      .andThen(ChainAction.get[CharRequest].chainedAction("char value is "))
      // Async should take a StringRequest => Result, but instead it takes a WrappedRequest => Result.
      .async { request: StringRequest[AnyContent, CharRequest[AnyContent]] =>
        Future.successful(Results.Ok(request.str + request.prevReq.char))
      }
  }
}

Не компилируется с ошибками:

[error] /home/matthew-work/temp/chain/chain/app/actions/ChainAction.scala:59:75: type mismatch;
[error]  found   : actions.StringRequest[play.api.mvc.AnyContent,actions.IntRequest[play.api.mvc.AnyContent]] => scala.concurrent.Future[play.api.mvc.Result]
[error]  required: play.api.mvc.WrappedRequest[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result]
[error]       .async { request: StringRequest[AnyContent, IntRequest[AnyContent]] =>
[error]                                                                           ^
[error] /home/matthew-work/temp/chain/chain/app/actions/ChainAction.scala:68:76: type mismatch;
[error]  found   : actions.StringRequest[play.api.mvc.AnyContent,actions.CharRequest[play.api.mvc.AnyContent]] => scala.concurrent.Future[play.api.mvc.Result]
[error]  required: play.api.mvc.WrappedRequest[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result]
[error]       .async { request: StringRequest[AnyContent, CharRequest[AnyContent]] =>
[error]                                                                            ^

build.sbt:

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.12.8"

libraryDependencies += guice

plugins.sbt

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.0")
addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0")

Scala версия 2.11.8, версия воспроизведения 2.8.0, sbt 1.3.8 и JVM openjdk-1.8.0.

...