gpt4 book ai didi

scala - 使用Play的ActionBuilder时如何包装Action(以任何顺序包装)?

转载 作者:行者123 更新时间:2023-12-04 03:37:48 26 4
gpt4 key购买 nike

我正在使用Play's ActionBuilder创建各种保护我的 Controller 的 Action 。例如,我实现了IsAuthenticated以确保只有在用户登录后才能访问某些操作:

case class AuthRequest[A](user: String, request: Request[A]) extends WrappedRequest[A](request)

private[controllers] object IsAuthenticated extends ActionBuilder[AuthRequest] {
def invokeBlock[A](req: Request[A], block: (AuthRequest[A]) => Future[SimpleResult]) = {
req.session.get("user").map { user =>
block(new AuthRequest(user, req))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}
}

使用 IsAuthenticated,我可以(1)将操作限制为已登录的用户,并且(b)访问正在登录的用户:
def auth = IsAuthenticated { implicit authRequest =>
val user = authRequest.user
Ok(user)
}

此外,我使用ActionBuilder HasToken来确保在请求的 header 中存在 token 的情况下调用了操作(并且,我可以访问 token 值):
case class TokenRequest[A](token: String, request: Request[A]) extends WrappedRequest[A](request)

private[controllers] object HasToken extends ActionBuilder[TokenRequest] {
def invokeBlock[A](request: Request[A], block: (TokenRequest[A]) => Future[SimpleResult]) = {
request.headers.get("X-TOKEN") map { token =>
block(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}
}

这样,我可以确保在存在该 token 的情况下调用了一个 Action :
def token = HasToken { implicit tokeRequest =>
val token = tokeRequest.token
Ok(token)
}

到现在为止还挺好...

但是,我如何包装(或嵌套/编写)上述定义的 Action ?例如,我想确保(a)将登录用户,并且(b)将存在 token :
def tokenAndAuth = HasToken { implicit tokeRequest =>
IsAuthenticated { implicit authRequest =>
val token = tokeRequest.token
val user = authRequest.user
}
}

但是,以上操作无法编译。我尝试了许多不同的实现,但始终未能实现所需的行为。

一般而言: 如何以任意顺序撰写使用Play的Action定义的ActionBuilder在上面的示例中,我将 IsAuthenticated包装在 HasToken中还是其他方法都没关系-效果是相同的:用户将必须登录并必须提供 token 。

注意:我创建了一个 Gist that provides the complete source code

最佳答案

Action 构建器

ActionBuilder不是为临时组合而创建的,而是为了构建 Action 的层次结构,因此最终您在整个 Controller 中只使用了几个 Action 。

因此,在您的示例中,您应该像illustrated here一样在IsAuthenticated之上构建HasToken

这是一个可行的解决方案,实际上可以简化您的代码。您真正需要多久在现场撰写一次?

基本 Action

临时组成可以通过EssentialActions实现(仅仅是因为它们没有从2.1更改),但是正如Johan所指出的那样,它们还有一些缺点。它们的API也不是专门用于临时使用的,并且Iteratees对于 Controller 操作而言太底层且太麻烦。

Action

因此,最后您的最后一个选择是直接编写 Action 。 Action 默认情况下不支持传递WrappedRequest(这就是存在ActionBuilder的原因)。但是,您仍然可以传递WrappedRequest并让下一个Action处理它。

以下是到目前为止我能想到的最好的方法,我想这是相当脆弱的。

case class HasToken[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.headers.get("X-TOKEN") map { token =>
action(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}

lazy val parser = action.parser
}

case class IsAuthenticated[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.session.get("user").map { user =>
action(new AuthRequest(user, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}

lazy val parser = action.parser
}

object ActionComposition extends Controller {
def myAction = HasToken {
Action.async(parse.empty) { case TokenRequest(token, request) =>
Future {
Ok(token)
}
}
}

def myOtherAction = IsAuthenticated {
Action(parse.json) { case AuthRequest(user, request) =>
Ok
}
}

def both = HasToken {
IsAuthenticated {
Action(parse.empty) { case AuthRequest(user, request: TokenRequest[_]) =>
Ok(request.token)
}
}
}
}

结果

您也可以在“结果”级别进行撰写,并且仅使用内置操作。这在尝试排除错误处理和其他重复性内容时特别有用。我有一个 example here

结论

我们仍然缺少Play 2.1的 Action 合成所提供的功能。到目前为止,对我来说,ActionBuilder + Result组合似乎是继任者。

关于scala - 使用Play的ActionBuilder时如何包装Action(以任何顺序包装)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21678812/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com