在播放动作中使隐式可用
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
}
我的应用程序命中许多不同的数据库,哪些数据库取决于查询字符串参数。我有一个接受字符串和 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
}