我正在开发一个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
(我想)这种方法的问题是我需要为每个控制器操作编写implicit request =>
和ClientContext(request)
打电话给MyDAO.findAll。
有没有办法通过Action包装器和隐式值来改进代码?我希望能够将context: ClientContext
声明为MyDAO.findAll
方法中的隐式参数,并按以下方式编写我的操作:
object MyDAO {
def findAll(implicit context: ClientContext) = { ... }
}
def index = ActionWithContext {
val results = MyDAO.findAll
Ok(results)
}
是否可以编写ActionWithContext(带有apply方法的方法或对象)来实现它?我现在最接近的是以下
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模式的好机会。答案 0 :(得分:3)
我使用了implicits实现了类似的东西:
我称之为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模块。事实上,我已经向您展示了两种可能性:
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
:
在Header
混合的对象中,此方法将在范围内。编译器将搜索要求UserInfo
对象位于Option[User]
已在范围内的范围的函数。编译器将隐式地调用withUserInfo
以提供缺少的UserInfo
对象。
请注意我的模板所需的隐式UserInfo
对象。当我调用此模板函数(Ok(...)
调用)时,编译器必须填写隐式UserInfo
对象。它将通过调用withUserInfo
并传递范围内的隐式maybeUser
来实现。
希望澄清一点含义。
答案 1 :(得分: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
,但这对我来说非常简洁。