无法用于理解以解决未来

时间:2018-09-17 17:51:00

标签: scala playframework-2.6

我有这个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?

    }
  }

1 个答案:

答案 0 :(得分:0)

您使用的是FutureOption,它们是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
}

有用的内容: