(本文译者是爱国者)
这章介绍几种定义action功能的方法。
假设我们需要记录每次action的调用,那么首先需要定义一个生成Action
对象的辅助函数, 并且在该函数中记录action的调用。
def LoggingAction(f: Request[AnyContent] => Result): Action[AnyContent] = {
Action { request =>
Logger.info("Calling action")
f(request)
}
}
然后使用刚定义的`LoggigAction`
def index = LoggingAction { request =>
Ok("Hello World")
}
修改一下上述的`LogginAction`,使其能被指定一个正文解析器:
def LoggingAction[A](bp: BodyParser[A])(f: Request[A] => Result): Action[A] = {
Action(bp) { request =>
Logger.info("Calling action")
f(request)
}
}
def index = LoggingAction(parse.text) { request =>
Ok("Hello World")
}
#### 包装现有的action
面使用包装模式给原生的`Action`添加额外功能:
case class Logging[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Result = {
Logger.info("Calling action")
action(request)
}
lazy val parser = action.parser
}
然后这样使用:
def index = Logging {
Action {
Ok("Hello World")
}
}
你仍然可以指定body parser:
def index = Logging {
Action(parse.text) {
Ok("Hello World")
}
}
> 另一种等价的写法:
def Logging[A](action: Action[A]): Action[A] = { Action(action.parser) { request => Logger.info("Calling action") action(request) } } #### 更复杂的例子 让我们看看一个更复杂但普通的权限验证的例子。这个例子中,我们需要使用包装过的body parser进行权限验证,并且向action代码块传递一个经过权限验证的user,并且。 def Authenticated[A](action: User => Action[A]): Action[A] = { // Let's define a helper function to retrieve a User def getUser(request: RequestHeader): Option[User] = { request.session.get("user").flatMap(u => User.find(u)) } // Wrap the original BodyParser with authentication val authenticatedBodyParser = parse.using { request => getUser(request).map(u => action(u).parser).getOrElse { parse.error(Unauthorized) } } // Now let's define the new Action Action(authenticatedBodyParser) { request => getUser(request).map(u => action(u)(request)).getOrElse { Unauthorized } } } 然后在controller中这样使用: def index = Authenticated { user => Action { request => Ok("Hello " + user.name) } }
注意: 在
play.api.mvc.Security.Authenticated
中已经有Authenticated
的更好实现。这里的例子做了简化#### 另一种创建Authenticated Action的方式 让我们看看如何在既不包装整个action也不authenticating the body parser的情况下改写上面的例子 def Authenticated(f: (User, Request[AnyContent]) => Result) = { Action { request => request.session.get("user").flatMap(u => User.find(u)).map { user => f(user, request) }.getOrElse(Unauthorized) } } def index = Authenticated { (user, request) => Ok("Hello " + user.name) } 这个例子存在的问题是无法将`request`声明为`implicit`. 可以采用curry的风格解决: def Authenticated(f: User => Request[AnyContent] => Result) = { Action { request => request.session.get("user").flatMap(u => User.find(u)).map { user => f(user)(request) }.getOrElse(Unauthorized) } } def index = Authenticated { user => implicit request => Ok("Hello " + user.name) } 另一种(也许更简单的)方法是定义一个`Request`的子类`AuthenticatedRequest`,这样我们就可以将两个参数合成一个参数: case class AuthenticatedRequest( val user: User, request: Request[AnyContent] ) extends WrappedRequest(request) def Authenticated(f: AuthenticatedRequest => Result) = { Action { request => request.session.get("user").flatMap(u => User.find(u)).map { user => f(AuthenticatedRequest(user, request)) }.getOrElse(Unauthorized) } } def index = Authenticated { implicit request => Ok("Hello " + request.user.name) } 对`Authenticated`函数重构一下,使其能指定BodyParser case class AuthenticatedRequest[A]( val user: User, request: Request[A] ) extends WrappedRequest(request) def Authenticated[A](p: BodyParser[A])(f: AuthenticatedRequest[A] => Result) = { Action(p) { request => request.session.get("user").flatMap(u => User.find(u)).map { user => f(AuthenticatedRequest(user, request)) }.getOrElse(Unauthorized) } } // Overloaded method to use the default body parser import play.api.mvc.BodyParsers._ def Authenticated(f: AuthenticatedRequest[AnyContent] => Result): Action[AnyContent] = { Authenticated(parse.anyContent)(f) }
原文:https://github.com/playframework/Play20/wiki/ScalaActionsComposition