我有这个Action
,它应该返回一个Future[Result]
,但是我无法使用for
理解来对其进行编码。这是我第一次使用for
理解,因此也不确定是否应该使用for
。
还有,有人会评论for
的用法是否正确吗?
def verifyUser(token:String) = Action.async{
implicit request => { //the function takes a token
val tokenFutureOption:Future[Option[UserToken]] = userTokenRepo.find(UserTokenKey(UUID.fromString(token))) //checkc if the token exists in the db (db returns a Future)
for(tokenOption<- tokenFutureOption) yield { //resolve the future
tokenOption match {
case Some(userToken) =>{//token exists
val userOptionFuture = userRepo.findUser(userToken.loginInfo)//find user to which the token belongs. Another db request which returns a Future
for(userOption <- userOptionFuture) yield {//resolve future
userOption match {
case Some(user) =>{//user exists
val newInternalProfile = user.profile.internalProfileDetails.get.copy(confirmed=true) //modify user's profile
val newProfile = UserProfile(Some(newInternalProfile),user.profile.externalProfileDetails)
val confirmedUser = user.copy(profile=newProfile)
val userOptionFuture :Future[Option[User]] = userRepo.updateUser(confirmedUser) //update profile with new value. Another db operation with returns a Future
for(userOption <- userOptionFuture) yield {//resolve future
userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))//remove the token
// Ok("user verified") //I WANT TO RETURN SUCCESS RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
}
}
case None =>{ //user doesn't exist
// Ok("user verified") //I WANT TO RETURN FAILURE RESPONSE HERE BUT CODE DOESN'T COMPILE IF I UNCOMMENT THIS
}
}
}
}
case None =>{//INVALID TOKEN RECEIVED
Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config //I CAN RETURN Redirect (WHICH IS OF SAME TYPE AS OK I.E. RESULT) BUT WHY AM I NOT ABLE TO USE OK ABOVE
}
}
}//THIS IS THE END OF FIRST FOR LOOP. HOW DO I HANDLE FAILURES IN THE FUTURE?
}
}
答案 0 :(得分:0)
您使用的是Future
,Option
,它们是Monads,它们背后的想法像Functor
s一样有助于对计算进行排序,还可以指定下一步将发生什么。 .flatMap
是Monad所允许的,可以用作for yield
以提高可读性。
因此在您的示例中,您可以使用一系列操作来组成api。
object Api {
final case class UserTokenKey(uuid: UUID)
final case class UserToken(loginInfo: String)
object userTokenRepo {
def find(u: UserTokenKey) = {
Future.successful(Some(UserToken(loginInfo = "foundLoginInfo")))
}
def remove(u: UserTokenKey) = {
Future.successful(Some(UserToken(loginInfo = "")))
}
}
final case class User(profile: String)
object userRepo {
def findUser(u: String) = {
Future.successful(Some(User("profile")))
}
def updateUser(u: User) = {
Future.successful(Some(User("updated profile")))
}
}
def verifyUser(token: String)(implicit executionContext: ExecutionContext) = {
val user: Future[Option[UserToken]] = for {
tokenMaybe: Option[UserToken] <- userTokenRepo.find(UserTokenKey(UUID.fromString(token)))
userMaybe: Option[User] <-
tokenMaybe match {
case Some(t) => userRepo.findUser(t.loginInfo)
case _ => Future.successful(Option.empty[User])
}
updatedUserMaybe: Option[User] <-
userMaybe match {
case Some(u) => userRepo.updateUser(u)
case _ => Future.successful(Option.empty[User])
}
removedUserMaybe: Option[UserToken] <- userTokenRepo.remove(UserTokenKey(UUID.fromString(token)))
} yield removedUserMaybe
user
}
def httpLayer(request: String) = {
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.mvc.Results
verifyUser(request).onComplete {
case Success(Some(user)) =>
println("user verified")
Results.Ok("user verified")
case Success(None) =>
println("user does not exist")
Results.Ok("user does not exist")
case Failure(f) =>
println("unknown error")
Results.Ok("unknown error")
}
}
}
现在您可以按以下方式测试您的api
def main(args: Array[String]): Unit = {
import Api._
httpLayer(UUID.randomUUID().toString) // user verified
}
Note1 :您可能想使用api来响应Future[Either[Error, User]]
以更好地处理错误,并使用Error
确定要处理的内容回复http使用者。
注意2 :由于您正在使用两个Future[Option[a]]
单子,所以可以使用Monad Transformation中的scalaz OptionT[OuterMonad, Value Type]
或cats mtl库将它们合并。给出一个单子OptionT
。
def verifyUserV2(tokenString: String)(implicit executionContext: ExecutionContext) = {
import scalaz._
import Scalaz._
// import cats.implicits._
// import cats.data.OptionT
val userStack = for {
token <- OptionT(userTokenRepo.find(UserTokenKey(UUID.fromString(tokenString))))
user <- OptionT(userRepo.findUser(token.loginInfo))
updatedUser <- OptionT(userRepo.updateUser(user))
removedUser <- OptionT(userTokenRepo.remove(UserTokenKey(UUID.fromString(tokenString))))
} yield removedUser
userStack.run
//cats unpack stack
// userStack.value
}
有用的内容: