Play framework + Scala:使用动作组合注入依赖

Posted

技术标签:

【中文标题】Play framework + Scala:使用动作组合注入依赖【英文标题】:Play framework + Scala: Inject dependency using Action Composition 【发布时间】: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 意思是,类似伪代码中的内容是理想的,然后我们必须根据需要添加更多代码以使其编译。对困惑感到抱歉。我会澄清的。 【参考方案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 提供程序 - 但您可以实现它。

以上是关于Play framework + Scala:使用动作组合注入依赖的主要内容,如果未能解决你的问题,请参考以下文章

如何将 IntelliJ 与 Play Framework 和 Scala 一起使用

Scala Play Framework,在哪里创建演员?

Play Framework Routes 中的 Scala 反引号

在 Play Framework 2.4 中为 Scala 实现 Akka

Scala Play Framework Slick 会话不起作用

Play Framework 2.4 在 Scala 模板中使用注入变量