3

我正在做一个 play 2.1 项目,需要一些关于 scala 设计问题的指导。对于我们的应用程序,模型层需要一个存储来自传入请求的客户端信息的请求上下文对象。

case class ClientContext(clientName: String) 

object ClientContext {
  def apply(request: Request) = {
    new ClientContext(request.params("clientName")) //pseudo code
  }
}

我的模型

object MyDAO {
  def findAll(context: ClientContext) = { ... }
}

然后在我们的控制器中,我们需要将它传递给模型的 dao 方法:

object MyController extends Controller {
  def index = Action { implicit request => 
    val results = MyDAO.findAll(ClientContext(request))
    Ok(results)
  }
}

由类implicit request提供Action(我想)这种方法的问题是我需要为调用 MyDAO.findAll 的每个控制器操作编写implicit request =>和。ClientContext(request)

有没有办法通过 Action 包装器和隐式值来改进代码?我希望能够context: ClientContext在方法中声明为隐式参数MyDAO.findAll并按以下方式编写我的操作:

object MyDAO {
  def findAll(implicit context: ClientContext) = { ... }
}

def index = ActionWithContext {
  val results = MyDAO.findAll
  Ok(results)
} 

是否可以编写一个 ActionWithContext (具有应用方法的方法或对象)来实现这一点?我现在最接近的是以下

def ActionWithContext(action: ClientContext => Result) = {
  Action { implicit request =>
    action(ClientContext(request))
  }
} 

用法

def index = ActionWithContext { context => 
  val results = MyDAO.findAll(context)
  Ok(results)
}

任何改进此设计的建议都会有所帮助。谢谢!

PS:老实说,如果这是在 Java 上,我什至不会考虑进一步简化代码,但由于它是 scala,我认为这可能是学习一些 scala 模式的好机会。

4

2 回答 2

3

我已经使用隐式实现了类似的东西:

我打电话给我Header而不是,Context但我们都在完成同样的事情。

我所有的控制器都混合了这个Header特征:

object Accounts extends AuthController with Header { ... }

我的Header特质看起来像这样:

trait Header {
    implicit def withUserInfo(implicit maybeUser: Option[User]): UserInfo = {
        // create user info object
    }
}

然后我可以像这样编写我的控制器操作:

def index = MaybeAuthenticated { implicit maybeUser => implicit request =>
    // do stuff
    val foo = new Foo()
    Ok(views.html.accounts.index(foo))
}

模板具有如下方法签名的地方:

@(foo: Foo)(implicit userInfo: UserInfo)

MaybeAuthenticated只是一个可选择恢复User对象的动作,它来自 play20-auth 模块。事实上,我在这里向您展示了两种可能性:

  1. 将特征与带有隐式参数的隐式函数混合。
  2. 编写自己的操作方法,例如MaybeAuthenticated

MaybeAuthenticated看起来像这样:

private def maybeAuthenticated(f: Option[Account] => Request[AnyContent] => Result): Action[AnyContent] = {
    Action(BodyParsers.parse.anyContent)(req => f(restoreUser(req))(req))
}

protected def MaybeAuthenticated = maybeAuthenticated _

我认为第一种方法更容易理解。

编辑:我认为有必要对隐式进行一些进一步的解释。

让我们考虑一下implicit上面使用的位置:

隐式 def withUserInfo(隐式可能用户:选项 [用户]):用户信息

在混入的对象中Header,此方法将在范围内。编译器将搜索要求UserInfo对象在 aOption[User]已经在范围内的范围内的函数。编译器将隐式调用withUserInfo以提供缺少的UserInfo对象。

UserInfo请注意我的模板所需的隐式对象。当我调用这个模板函数(Ok(...)调用)时,编译器必须填写隐式UserInfo对象。它将通过调用withUserInfo和传递范围内的隐式来实现maybeUser

希望澄清一点。

于 2013-03-22T19:18:56.163 回答
1

感谢 Ryan 的建议,这里是另一个解决方案在 ClientContext 对象中使 apply 方法也是一个隐式转换,它接受一个隐式参数

object ClientContext {
  implicit def apply(implicit request: Request) = {
    new ClientContext(request.params("clientName")) //pseudo code
  }
}

然后在控制器中你可以写

def index = Action { implicit request => 
  val results = MyDAO.findAll
  Ok(results)
}

我不确定是否有办法摆脱它,implicit request但现在这对我来说非常简洁。

于 2013-03-22T22:35:27.383 回答