【问题标题】:Play framework + Scala: Inject dependency using Action CompositionPlay framework + Scala:使用动作组合注入依赖
【发布时间】:2014-10-23 19:26:47
【问题描述】:

我正在设置 Play!我们的 API 应用程序。该 API 封装了不同的服务。我想将这些服务注入到一个动作中,但只注入该特定端点所需的那些。比如:

object Application extends Controller {
  def index = (UsersAction andThen OrdersAction) {
    // boom UsersService and OrdersService is available here
    for {
      users <- usersService.list
      orders <- ordersService.list
    } yield "whatever"
  }
}

我一直在玩这个想法并使用 ActionTransformers 我能够将传入的请求转换为具有给定服务的请求,但我不知道如何使该请求足够通用,以便我可以编写这些以任意顺序执行操作,而不为 WrapperRequests 的所有可能组合创建 ActionTransformers。

也许动作组合并不是实现这一目标的最佳方式。我全神贯注。

谢谢

更新:

为了澄清,上面的代码是伪代码,理想的场景,其中 usersService 和 ordersService 可用于该范围(隐含?我不知道)。如果那是不可能的,那么无论在该样本顶部添加较少量的噪声,都可以。谢谢

【问题讨论】:

  • 你有显示UsersActionOrdersAction 示例的代码(简化版)
  • 这只是伪代码。我一直在玩动作组合,试图按照这里的例子做一些事情:playframework.com/documentation/2.3.5/ScalaActionsComposition
  • 我问的原因是我看不到您打算如何使usersServiceordersService 可用。您想在哪里指定它们以及如何使它们可用于您的操作主体?
  • 这是问题的一部分 ^^U 意思是,类似伪代码中的内容是理想的,然后我们必须根据需要添加更多代码以使其编译。对困惑感到抱歉。我会澄清的。

标签: scala dependency-injection playframework


【解决方案1】:

我最接近您的问题的是:

def index =
  new UsersAction with OrdersAction {
    def body =
      for {
        users <- userService.list
        orders <- orderService.list
      } yield Ok("whatever")
  }

实现非常简单

trait CustomAction extends Action[AnyContent] {
  def body: Future[Result]

  def apply(request: Request[AnyContent]): Future[Result] = body
  val parser = BodyParsers.parse.anyContent
}

trait UsersAction extends CustomAction {
  val userService: UserService = ???
}
trait OrdersAction extends CustomAction {
  val orderService: OrderService = ???
}

这些是我用来编译它的其他部分:

trait User
trait Order

trait UserService {
  def list: Future[Seq[User]]
}

trait OrderService {
  def list: Future[Seq[Order]]
}

【讨论】:

  • 哇,谢谢!我会将其标记为已接受。我看到这种方法的唯一问题(我认为)是我们失去了执行 Action.asycn { } 等的能力。这能解决吗?
  • async,正文输入为Future[Result]
【解决方案2】:

您可以通过 guice、spring 或您想要的方式进行注入。 guice 的例子。 只需将对象更改为类:

class Application @Inject(userAction:UsersAction,ordersAction:OrdersAction) extends Controller {
  def index = (UsersAction andThen OrdersAction) {
    // boom UsersService and OrdersService is available here
    for {
      users <- usersService.list
      orders <- ordersService.list
    } yield "whatever"
  }
}

您必须在全局中覆盖:

object Global extends GlobalSettings{

 private lazy val injector = Guice.createInjector(new CommonModule)

 override def getControllerInstance[A](clazz: Class[A]) = {
    injector.getInstance(clazz)
  }

}
class CommonModule extends AbstractModule{

  protected def configure() {
    bind(classOf[UsersAction]).to(classOf[UsersActionImpl])
    bind(classOf[OrdersAction]).to(classOf[OrdersActionImpl])
  }

}

在路由文件中添加@到控制器:

GET /service  @controllers.Application.index

【讨论】:

  • 感谢您的回复。我希望在端点的基础上注入每个依赖项,而不是在控制器中全局注入。换句话说,我只希望某个端点能够访问给定的依赖项,而不使该依赖项可用于同一控制器中的端点。
  • 那为什么不使用不同的控制器呢?
  • 如果您想要请求特定服务 - 只需使用 [工厂方法设计模式](或带有输入参数的修改版本)或提供程序。在游戏世界中 - 您不能使用请求范围 guice 提供程序 - 但您可以实现它。
猜你喜欢
  • 1970-01-01
  • 2019-08-16
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多