如何映射 Future 的失败投影
how to map the failed projection of a Future
在下面的代码中,函数应该是 Future[Result]
的 return 实例,但我无法这样做。代码查询数据库,数据库returnsFuture[User]
。我想我能够正确地描绘未来的成功部分,但不能正确描绘失败的部分。请看函数末尾的注释。
def addUser = silhouette.UserAwareAction.async{ implicit request => {
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson
//check for message body. It should be json
jsonBody match {
case Some(json) => { //got json in message body.
val readableString: String = Json.prettyPrint(json)
println(s"received Json ${readableString}")
val userProfile: Option[UserProfile] = json.asOpt[UserProfile] //check if json conforms with UserProfile structure
userProfile match {
case Some(profile) => { //json conforms to UserProfile.
println(s"received profile ${profile}")
val loginInfo = LoginInfo(CredentialsProvider.ID, profile.externalProfileDetails.email)
println(s"checking if the user with the following details exists ${loginInfo}")
val userFuture: Future[Option[User]] = userRepo.find(loginInfo) // userFuture will eventually contain the result of database query i.e Some(user) or None
userFuture.map { user:Option[User] => { //Future successful
case Some(user) => { //duplicate user
println("duplicate user" + user)
Future { Ok(Json.toJson(JsonResultError("duplicate user"))) }
}
case None => { //unique user
Future { Ok(Json.toJson(JsonResultSuccess("Not duplicate user"))) }
}
}
}
/***This is the part I am unable to code. I suppose the addUser method expect that the last statement (its return value) is Future{....} but it seems that they way I have coded it, it is not the case. If I remove this code and just type Future { Ok(Json.toJson(JsonResultSuccess("Internal Server Error"))) } then code compiles. But that logic is not correct because then this message will be sent all the time, not when the future fails!***/
val userFailedFuture:Future[Throwable] = userFuture.failed
userFailedFuture.map {x=> Future { Ok(Json.toJson(JsonResultSuccess("Internal Server Error"))) }}
}
//Json doesn't conform to UserProfile
case None => Future { Ok(Json.toJson(JsonResultError("Invalid profile"))) } /*TODOM - Standardise error messages. Use as constants*/
}
}
//message body is not json. Error.
case None => Future { Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json"))) }/*TODOM - Standardise error messages. Use as constants*/
}
}
}
你不必在未来包装你的结果,因为你已经在根据未来的价值进行评估,这很简单:
futureValue.map{value => //RESULT
}
以及Future中的错误处理,推荐使用recover with map like:
futureValue.map{value => //RESULT
}.recover{case ex: Exception => //RESULT
}
如果结果已经在映射或恢复块中,则无需将结果包装在 Future 中。作为 Future 的 map 和 recover 之外的最终结果将是 Future[Result]。所以如果你包装另一个 Future 它将变成 Future[Future[Result]].
同意@geek94 的回答。
一次修正:
不清楚您使用的是什么库,但在大多数情况下不需要显式调用 future.recover
。每个体面的 http 库(例如 akka-http)都将失败的未来视为 InternalServerError。
在下面的代码中,函数应该是 Future[Result]
的 return 实例,但我无法这样做。代码查询数据库,数据库returnsFuture[User]
。我想我能够正确地描绘未来的成功部分,但不能正确描绘失败的部分。请看函数末尾的注释。
def addUser = silhouette.UserAwareAction.async{ implicit request => {
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson
//check for message body. It should be json
jsonBody match {
case Some(json) => { //got json in message body.
val readableString: String = Json.prettyPrint(json)
println(s"received Json ${readableString}")
val userProfile: Option[UserProfile] = json.asOpt[UserProfile] //check if json conforms with UserProfile structure
userProfile match {
case Some(profile) => { //json conforms to UserProfile.
println(s"received profile ${profile}")
val loginInfo = LoginInfo(CredentialsProvider.ID, profile.externalProfileDetails.email)
println(s"checking if the user with the following details exists ${loginInfo}")
val userFuture: Future[Option[User]] = userRepo.find(loginInfo) // userFuture will eventually contain the result of database query i.e Some(user) or None
userFuture.map { user:Option[User] => { //Future successful
case Some(user) => { //duplicate user
println("duplicate user" + user)
Future { Ok(Json.toJson(JsonResultError("duplicate user"))) }
}
case None => { //unique user
Future { Ok(Json.toJson(JsonResultSuccess("Not duplicate user"))) }
}
}
}
/***This is the part I am unable to code. I suppose the addUser method expect that the last statement (its return value) is Future{....} but it seems that they way I have coded it, it is not the case. If I remove this code and just type Future { Ok(Json.toJson(JsonResultSuccess("Internal Server Error"))) } then code compiles. But that logic is not correct because then this message will be sent all the time, not when the future fails!***/
val userFailedFuture:Future[Throwable] = userFuture.failed
userFailedFuture.map {x=> Future { Ok(Json.toJson(JsonResultSuccess("Internal Server Error"))) }}
}
//Json doesn't conform to UserProfile
case None => Future { Ok(Json.toJson(JsonResultError("Invalid profile"))) } /*TODOM - Standardise error messages. Use as constants*/
}
}
//message body is not json. Error.
case None => Future { Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json"))) }/*TODOM - Standardise error messages. Use as constants*/
}
}
}
你不必在未来包装你的结果,因为你已经在根据未来的价值进行评估,这很简单:
futureValue.map{value => //RESULT
}
以及Future中的错误处理,推荐使用recover with map like:
futureValue.map{value => //RESULT
}.recover{case ex: Exception => //RESULT
}
如果结果已经在映射或恢复块中,则无需将结果包装在 Future 中。作为 Future 的 map 和 recover 之外的最终结果将是 Future[Result]。所以如果你包装另一个 Future 它将变成 Future[Future[Result]].
同意@geek94 的回答。 一次修正:
不清楚您使用的是什么库,但在大多数情况下不需要显式调用 future.recover
。每个体面的 http 库(例如 akka-http)都将失败的未来视为 InternalServerError。