Scala: How to get the result of a Future -
i've method returns future
this...
def istokenexpired(token: string): future[boolean] = { ... }
... , i've method invokes istokenexpired
returns boolean
this:
def isexpired(token: string): boolean = { var result = true istokenexpired(token).oncomplete { case success(r) => result = r case failure(_) => result = true } result }
is there better way write isexpired
method?
edit
as requested eecolor, let me provide more details. play application i've implemented authorization mechanism based on json web token (jwt). claims contained in jwt except expiration time, stored in mongodb collection. here below summary of how token
class looks like:
class token { ... def id: string = { ... } def issuetime: localdatetime = { ... } def issuer: string = { ... } ... def isvalid: boolean = { ... } def isexpired: boolean = { /* uses reactivemongo access mongodb */ } }
as can see, jwt properties self-contained except expiration info. method isexpired
uses reactivemongo, returns future
. make things more complex, use jwt in customized action
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 account ${token.subject}") future.successful(forbidden(autherrors.requestnotauthorized(token.subject)(request).asjson)) } else { logger.debug(s"request ${request.uri} authorized 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)) } } }
as can see, need return future[play.mvc.results.result]
, not future[boolean]
as return isexpired
if used future.map
. point?
the function wrote not work think. (likely) first return true
, later set result
variable.
normally this:
istokenexpired(token).map { result => // stuff }
in framework play map future
http response , give play future[simpleresult]
. play knows how handle future
results.
in general it's recommended not wait future
complete in production code, work values in future
, let framework using handle result.
in tests might come in handy wait result, can this:
await.result(somefuture, 5.seconds)
edit
i extract construction of token end future[token]
. allows me more compose things. allows me create code has better architecture , easier test.
i break down code more smaller methods, example below gives idea of direction 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 account ${token.subject}") future.successful(forbidden(autherrors.requestnotauthorized(token.subject)(request).asjson)) case token => logger.debug(s"request ${request.uri} authorized 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 } } }
Comments
Post a Comment