Scala:如何获得未来的结果 [英] Scala: How to get the result of a Future
问题描述
我有一个返回这样的 Future
的方法...
I've a method that returns a Future
like this...
def isTokenExpired(token: String): Future[Boolean] = {
...
}
...然后我有另一个方法调用 isTokenExpired
,该方法返回如下的 Boolean
:
... and then I've another method that invokes isTokenExpired
that returns a Boolean
like this:
def isExpired(token: String): Boolean = {
var result = true
isTokenExpired(token).onComplete {
case Success(r) => result = r
case Failure(_) => result = true
}
result
}
是否有更好的方式编写 isExpired
方法?
Is there a better way to write the isExpired
method?
编辑
根据EECOLOR的要求,让我为您提供更多详细信息.对于我的Play应用程序,我已经实现了基于JSON Web令牌(jwt)的授权机制.除到期时间外,所有声明都包含在jwt中,到期时间存储在MongoDB集合中.以下是我的 Token
类的摘要:
As requested by EECOLOR, let me provide you with more details. For my Play application I've implemented an authorization mechanism based on JSON Web Token (jwt). All the claims are contained in the jwt except the expiration time, which is stored in a MongoDB collection. Here below is a summary of how my Token
class looks like:
class Token {
...
def id: String = { ... }
def issueTime: LocalDateTime = { ... }
def issuer: String = { ... }
...
def isValid: Boolean = { ... }
def isExpired: Boolean = { /* uses ReactiveMongo to access MongoDB */ }
}
如您所见,除到期信息外,所有jwt属性都是独立的.方法 isExpired
使用ReactiveMongo,该方法始终返回 Future
.为了使事情变得更加复杂,我在自定义的 Action
中使用此jwt,如下所示:
As you can see, all the jwt properties are self-contained except expiration info. Method isExpired
uses ReactiveMongo, which always returns a Future
. To make things even more complex, I use this jwt in a customized Action
like this:
class SecuredAction[T <: Controller] private(private val methodName: String)
extends ActionBuilder[ApiRequest] {
...
def invokeBlock[A](request: Request[A], block: (ApiRequest[A]) => Future[SimpleResult]) = {{
request.headers.get(HeaderNames.AUTHORIZATION) match {
case Some(header) => s"""$AuthType (.*)""".r.unapplySeq(header).map(_.head.trim)
case _ => None
}} match {
case Some(tokenString) => {
val token = Token(tokenString)
if (!token.isValid) {
Logger.warn(s"request ${request.uri} not authorized: token ${token.id} has been tampered")
Future.successful(Unauthorized(AuthErrors.authenticationViolated(token.subject)(request).asJson))
} else if (token.isExpired) {
Logger.debug(s"request ${request.uri} not authorized: token ${token.id} has expired")
Future.successful(Unauthorized(AuthErrors.authenticationExpired(token.subject)(request).asJson))
} else if (!isAuthorized(token)) {
Logger.info(s"request ${request.uri} not authorized: required claims not defined for account ${token.subject}")
Future.successful(Forbidden(AuthErrors.requestNotAuthorized(token.subject)(request).asJson))
} else {
Logger.debug(s"request ${request.uri} authorized for account ${token.subject}")
block(new ApiRequest(token, request))
}
}
case _ => {
Logger.debug(s"request ${request.uri} not authenticated")
Future.successful(Unauthorized(
AuthErrors.requestNotAuthenticated()(request).asJson
).withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthType))
}
}
}
如您所见,我需要返回一个 Future [play.mvc.results.Result]
,而不是一个 Future [Boolean]
,因为它会返回如果我使用的是
.你能指出吗? Future.map
,则为isExpired
As you can see, I need to return a Future[play.mvc.results.Result]
, not a Future[Boolean]
as would return isExpired
if I used Future.map
. Do you get to point?
推荐答案
您编写的函数将无法正常运行.(可能)首先返回 true
,然后设置 result
变量.
The function you wrote will not work as you would think. It would (likely) first return true
and later set the result
variable.
通常,您会执行以下操作:
Normally you would do something like this:
isTokenExpired(token).map { result =>
// do stuff
}
在类似Play的框架中,您会将 Future
映射到http响应,并给Play播放一个 Future [SimpleResult]
.Play知道如何处理 Future
结果.
In a framework like Play you would map the Future
to an http response and give Play back a Future[SimpleResult]
. Play knows how to handle Future
results.
通常,建议您不要等待 Future
完成生产代码,而是使用 Future
中的值并让您使用的框架处理结果.
In general it's recommended you do not wait for a Future
to complete in production code, but work with the values in the Future
and let the framework you are using handle the result.
在测试中,等待结果可能会派上用场,您可以这样做:
In tests it might come in handy to wait for a result, you can do that like this:
Await.result(someFuture, 5.seconds)
修改
我可能会提取令牌的构造,以便最终得到 Future [Token]
.这使我可以更轻松地编写内容.它还使我可以创建具有更好的体系结构并且更易于测试的代码.
I would probably extract the construction of a token so that I end up with a Future[Token]
. That allows me to more easily compose things. It also allows me to create code that has a better architecture and is easier to test.
我可能会将代码分解为更小的方法,但是下面的示例使您对我的发展方向有所了解.
I would probably break down the code into more smaller methods, but the example below gives you an idea of the direction I would take.
class TokenService(connection: MongoConnection) {
def tokenFor(tokenString: String): Future[Token] = ???
}
class SecuredAction(tokenService: TokenService) extends
ActionBuilder[ApiRequest] {
import play.api.libs.concurrent.Execution.Implicits._
def invokeBlock[A](request: Request[A], block: (ApiRequest[A]) => Future[SimpleResult]) =
extractTokenFrom(request) match {
case Some(tokenString) => {
tokenService.tokenFor(tokenString) flatMap {
case token if (!token.isValid) =>
Logger.warn(s"request ${request.uri} not authorized: token ${token.id} has been tampered")
Future.successful(Unauthorized(AuthErrors.authenticationViolated(token.subject)(request).asJson))
case token if (token.isExpired) =>
Logger.debug(s"request ${request.uri} not authorized: token ${token.id} has expired")
Future.successful(Unauthorized(AuthErrors.authenticationExpired(token.subject)(request).asJson))
case token if (!token.isAuthorized) =>
Logger.info(s"request ${request.uri} not authorized: required claims not defined for account ${token.subject}")
Future.successful(Forbidden(AuthErrors.requestNotAuthorized(token.subject)(request).asJson))
case token =>
Logger.debug(s"request ${request.uri} authorized for account ${token.subject}")
block(new ApiRequest(token, request))
}
}
case _ =>
Logger.debug(s"request ${request.uri} not authenticated")
Future.successful(Unauthorized(
AuthErrors.requestNotAuthenticated()(request).asJson).withHeaders(HeaderNames.WWW_AUTHENTICATE -> AuthType))
}
val AuthType = "MyAuthType"
val TokenHeader = s"""$AuthType (.*)""".r
def extractTokenFrom(request: RequestHeader) = {
val authorizationHeader = request.headers.get(HeaderNames.AUTHORIZATION)
authorizationHeader flatMap {
case TokenHeader(token) => Some(token.trim)
case _ => None
}
}
}
这篇关于Scala:如何获得未来的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!