我对scala和play-framework中的函数式编程非常陌生
我有一个API请求,需要在该特定用户创作的漫画书中添加章节,因此我必须验证漫画是否存在并且属于该用户。更复杂的是Future
,Option
等,我无法确保最后一个命令执行完毕。实际上,我的问题发生是因为在我的所有数据库访问完成之前执行了最后一个命令
以下是代码,希望很清楚
def addchapter(): Action[AnyContent] = Action.async { implicit request =>
var hashMap: HashMap[String, JsValue] = HashMap()
var apiResult: ApiResult = ApiResult(
ReturnCode.COMIC_ADDCHAPTER.id,
ReturnCode.COMIC_ADDCHAPTER.toString(),
ReturnResult.RESULT_ERROR.toString(),
"init"
)
var fHashMap: Future[HashMap[String, JsValue]] = Future {
hashMap
}
try {
val oReq = request.body.asJson
val jsReq = oReq.getOrElse(JsString("null"))
val sessionid = (jsReq \
"sessionid").getOrElse(JsString("0")).as[String]
val comicid = (jsReq \
"comicid").getOrElse(JsString("comicid")).as[String]
val foUser = userRepo.getUserBySessionId(sessionid)
LogManager.DebugLog(this, "add chapter: " + sessionid + " => " +
comicid)
fHashMap = foUser.flatMap( oUser => {
oUser match {
case Some(user) => {
val foComic = comicRepo.getComicByComicId(comicid)
fHashMap = foComic.flatMap( oComic => {
oComic match {
case Some(comic) => {
LogManager.DebugLog(this, "about to add chapter")
val fTup = comicRepo.addChapterToComic(comic)
fHashMap = fTup.map( tuple => {
val wr = tuple._1
val mc = tuple._2
apiResult = ApiResult(
ReturnCode.COMIC_ADDCHAPTER.id,
ReturnCode.COMIC_ADDCHAPTER.toString(),
ReturnResult.RESULT_ERROR.toString(),
"successfully added chapter!"
)
val payloadcomic =
PayloadComicFactory.createWithComic(mc)
hashMap("apiresult") = Json.toJson(apiResult)
hashMap += "comic" -> Json.toJson(payloadcomic)
LogManager.DebugLog(this, "successfully added
chapter!")
hashMap
})
// return
fHashMap
}
case None => {
apiResult = ApiResult(
ReturnCode.COMIC_ADDCHAPTER.id,
ReturnCode.COMIC_ADDCHAPTER.toString(),
ReturnResult.RESULT_ERROR.toString(),
"comic not found"
)
hashMap("apiresult") = Json.toJson(apiResult)
Future { hashMap }
}
}
})
Future { hashMap }
}
case None => {
apiResult = ApiResult(
ReturnCode.COMIC_ADDCHAPTER.id,
ReturnCode.COMIC_ADDCHAPTER.toString(),
ReturnResult.RESULT_ERROR.toString(),
"unauthorized to add chapter to this comic"
)
hashMap("apiresult") = Json.toJson(apiResult)
Future { hashMap }
}
}
})
// I cannot put the return here, it is compile error saying that the return value is a Unit
// fHashMap.map( hashmap => {
// Ok(Json.toJson(hashmap))
// })
} catch {
case t: Throwable => {
LogManager.DebugException(this, "ex: ", t)
// I cannot put it here, it is compile error saying that the return value is Unit
// fHashMap.map( hashmap => {
// Ok(Json.toJson(hashmap))
// })
fHashMap
}
}
// I have to return here, but "return of the request" will be executed first before all my db access is completed, thus the ApiResult is still returning wrong state
LogManager.DebugLog(this, "return of the request")
fHashMap.map( hashmap => {
Ok(Json.toJson(hashmap))
})
}
答案 0 :(得分:0)
您可以尝试以下重构作为迈向更多Scala惯用方法的第一步:
def addchapter(): Action[AnyContent] = Action.async { implicit request =>
def apiResultJson(message: String) =
Json.toJson(ApiResult(
ReturnCode.COMIC_ADDCHAPTER.id,
ReturnCode.COMIC_ADDCHAPTER.toString(),
ReturnResult.RESULT_ERROR.toString(),
message))
def okResponse(hashMapF: Future[HashMap]) =
hasMapF.map(hashMap => Ok(Json.toJson(hashMap)))
Try {
val oReq = request.body.asJson
val jsReq = oReq.getOrElse(JsString("null"))
val sessionid = (jsReq \ "sessionid").getOrElse(JsString("0")).as[String]
val comicid = (jsReq \ "comicid").getOrElse(JsString("comicid")).as[String]
userRepo.getUserBySessionId(sessionid).flatMap {
case Some(user) =>
comicRepo.getComicByComicId(comicid).flatMap {
case Some(comic) =>
comicRepo.addChapterToComic(comic).map { tuple =>
val payloadcomic = PayloadComicFactory.createWithComic(tuple._2)
HashMap(
"apiresult" -> apiResultJson("successfully added chapter!"),
"comic" -> Json.toJson(payloadcomic))
}
case None =>
Future(HashMap("apiresult" -> apiResultJson("comic not found")))
}
case None =>
Future(HashMap(
"apiresult" -> apiResultJson("unauthorized to add chapter to this comic")))
}
} match {
case Success(resultHashMapF) =>
okResponse(resultHashMapF)
case Failure(t) =>
okResponse(Future(HashMap("apiresult" -> apiResultJson(t.toString))))
}
}