我正在开发一个 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) = { ... }
}
然后在我们的 Controller 中,我们需要将其传递到模型的 dao 方法中:
object MyController extends Controller {
def index = Action { implicit request =>
val results = MyDAO.findAll(ClientContext(request))
Ok(results)
}
}
隐式请求
由Action
类提供(我想)这种方法的问题是我需要编写隐式请求=>
和 ClientContext(request)
用于调用 MyDAO.findAll 的每个 Controller 操作。
有没有办法通过 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 模式的好机会。
最佳答案
我已经使用隐式实现了类似的东西:
我称我的 Header
而不是 Context
,但我们都在完成同样的事情。
我的所有 Controller 都混合了 Header
特征:
object Accounts extends AuthController with Header { ... }
我的 Header
特征看起来像这样:
trait Header {
implicit def withUserInfo(implicit maybeUser: Option[User]): UserInfo = {
// create user info object
}
}
然后我可以像这样编写我的 Controller 操作:
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 _
我认为第一种方法更容易理解。
编辑:我认为有必要对隐式进行进一步的解释。
让我们考虑一下上面使用隐式
的地方:
隐式 def withUserInfo(隐式 MaybeUser: Option[User]): UserInfo
在混合在 Header
中的对象中,此方法将在范围内。编译器将搜索需要 UserInfo
对象位于 Option[User]
已在范围内的函数。编译器将隐式调用withUserInfo
来提供缺少的UserInfo
对象。
请注意我的模板所需的隐式 UserInfo
对象。当我调用此模板函数(Ok(...)
调用)时,编译器必须填充隐式 UserInfo
对象。它将通过调用 withUserInfo
并传递范围内隐式 maybeUser
来实现此目的。
希望能够澄清一些隐含的内容。
关于具有隐式参数的 Scala 设计模式(在 Scala 中 Play 2.x),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15578044/