在播放动作中使隐式可用

making an implicit available within a play action

我的应用程序命中许多不同的数据库,哪些数据库取决于查询字符串参数。我有一个接受字符串和 returns 配置的 DatabaseConfigLocator,它工作得很好。我的问题是我想让每个请求的配置在我的控制器中隐式可用。我尝试了两种方法。

class MyController extends Controller{
  implicit def dbConfig(implicit request: RequestHeader): DatabaseConfig[JdbcProfile] = DatabaseConfigLocator.get[JdbcProfile](request.getQueryString("dbName")
}

除非我将它更改为具有相同类型的 implicit val,否则不会编译,但我需要在每个请求中重新检查查询字符串,而不是一次,所以我不认为 implicit val会工作

另一种方法是创建一个动作

object IODBAction extends ActionBuilder[Request]{
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    implicit def dbConfig(implicit request: RequestHeader): DatabaseConfig[JdbcProfile] = DatabaseConfigLocator.get[JdbcProfile]("blah")
    block(request)
  }
}

但是该隐式在块的范围内不可用,我找不到任何方法将其作为隐式传递。

我的目标是能够做这样的事情

class MyController extends Controller {

  def create = {
    Action.async {
      request =>
        ApiResponse {
          for {
            id <- aProvider.save(validRegistrationRequest.toVisitor)
          } yield id
        }
    }
  }
}

class aProvider {
  def save(v: Visitor)(implicit dbConfig: DatabaseConfig[JdbcProfile]): ApiResponse[VisitorId]

}

或者如果提供者可以在实例化时获得隐式更好

class aProvider(implicit dbConfig: DatabaseConfig[JdbcPRofile]) {
  def save(v: Visitor): ApiResponse[VisitorId]
}

关于如何解决这个问题或者如果可以使用 play 框架有什么建议吗?

不幸的是,我担心您被一个接收单个参数的动作困住了,所以您需要坚持标准的播放 "Action Composition" 模式。这在 play docs 中有相当广泛的记录。

我会这样定义自己 "Context":

case class Context(dbConfig: DatabaseConfig[JDBCProfile], request: Request[A])
    extends WrappedRequest(request)

然后像这样创建自定义操作生成器:

object DBIOAction extends ActionBuilder[Context]{
  def invokeBlock[A](request: Request[A], block: (Context[A]) => Future[Result]) = {
   val dbConfig = DatabaseConfigLocator.get[JdbcProfile]("blah")
   val context = Context(dbConfig, request)
    block(context)
  }
}

然后您应该可以像这样使用它:

def index = DBIOAction { implicit context =>
   // do some stuff. return a result
}

为了简单起见,我会将隐式上下文传递到您的服务方法中,也许会从上下文中提取 dbConfig,然后将其传递给您的 DAO。

class FunService { 
    def getSomeData(param1: String)(implicit context: Context) = {
        // do some work, perhaps using context.dbConfig
    }