此多任务操作的功能样式
Functional style for this multi-task Action
我有一个 Play 框架 Action,它需要按此顺序完成 3 件事:-
- 从请求中获取两个参数 (A, B)
- 如果两个参数都存在则使用 A 从缓存中检索条目
- 如果缓存中存在条目,则使用 B
调用 Web 服务
我目前拥有的:-
// async since we are calling Async APIs like WS
def myAction = Action.async { implicit request =>
val A = requestParam(request, "a") // Option[String]
val B = requestParam(request, "b")
val futureResponse = for {
token <- getFromCache(A) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with param or cache", e))
}
wsResponse <- webServiceCall(B) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with web service call", e))
}
} yield Ok(wsResponse.body)
futureResponse recover {
case e: Exception => Ok(failureBody(e.getMessage))
}
}
为了简洁起见,我显然省略了一些对我的问题不重要的细节。
我的问题是(作为 Scala 新手)完成此 Action 的实用方法是什么。如果参数不存在,那么我不想执行 for
理解,而是 return 包含错误消息的 Future[Result]
。目前我能想到的唯一方法是使用下面的 if
测试,但我不禁怀疑可能有 purer 方法来实现这个。
val A = requestParam(request, "a") // Option[String]
val B = requestParam(request, "b")
if(A.isEmpty || B.isEmpty) {
Future { Ok("Params missing") }
} else {
val futureResponse = for {
token <- getFromCache(A) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with param or cache", e))
}
wsResponse <- webServiceCall(B) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with web service call", e))
}
} yield Ok(wsResponse.body)
futureResponse recover {
case e: Exception => Ok(failureBody(e.getMessage))
}
}
对于人们如何更优雅地处理我的上述解决方案的任何评论,我将不胜感激。
正如您所注意到的,有很多方法可以处理依赖 Future
和 Option
的事情,这真的是一个品味问题。如果 if
表达式在给定情况下可以更清楚地传达您的观点,我认为使用 if
表达式并没有什么本质上的错误。那就是说你可以:
使用带有 .getOrElse
的 for
表达式来确保您的两个参数都得到满足(尽管嵌套的 for
有时看起来不太好)。稍微(不必要地)重写您的逻辑可能如下所示:
import scala.concurrent.Future.{successful => immediate}
def getFromCache(key: String, data: String): Future[String] = ???
def webServiceCall(key: String): Future[WSResponse] = ???
def myAction = Action.async { implicit request =>
(for {
a <- request.getQueryString("a")
b <- request.getQueryString("b")
} yield (for {
cachedData <- getFromCache(a)
r <- webServiceCall(b, cachedData)
} yield Ok(r.body)) recover {
case e => InternalServerError(e.getMessage)
}) getOrElse {
immediate(BadRequest("params missing"))
}
}
您还可以在 Play 中选择使用 Form
来确保多个参数存在且有效,因此您最终可能会得到类似以下的内容(再次略微压缩,根据需要展开):
import play.api.data.Forms._
import play.api.data.Form
val form = Form(tuple("a" -> nonEmptyText, "b" -> nonEmptyText))
def myAction2 = Action.async { implicit request =>
form.bindFromRequest.fold(
err => immediate(BadRequest("missing params")), { case (a, b) =>
getFromCache(a).flatMap { cachedData =>
webServiceCall(b, cachedData).map(r => Ok(r.body))
} recover {
case e => InternalServerError(e.getMessage)
}
}
)
}
关于这类事情的一些好帖子是:
- Error Handling in Scala
- How to compose Future and Option in Scala(在这种情况下可能有点多,但仍然很有趣。)
我有一个 Play 框架 Action,它需要按此顺序完成 3 件事:-
- 从请求中获取两个参数 (A, B)
- 如果两个参数都存在则使用 A 从缓存中检索条目
- 如果缓存中存在条目,则使用 B 调用 Web 服务
我目前拥有的:-
// async since we are calling Async APIs like WS
def myAction = Action.async { implicit request =>
val A = requestParam(request, "a") // Option[String]
val B = requestParam(request, "b")
val futureResponse = for {
token <- getFromCache(A) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with param or cache", e))
}
wsResponse <- webServiceCall(B) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with web service call", e))
}
} yield Ok(wsResponse.body)
futureResponse recover {
case e: Exception => Ok(failureBody(e.getMessage))
}
}
为了简洁起见,我显然省略了一些对我的问题不重要的细节。
我的问题是(作为 Scala 新手)完成此 Action 的实用方法是什么。如果参数不存在,那么我不想执行 for
理解,而是 return 包含错误消息的 Future[Result]
。目前我能想到的唯一方法是使用下面的 if
测试,但我不禁怀疑可能有 purer 方法来实现这个。
val A = requestParam(request, "a") // Option[String]
val B = requestParam(request, "b")
if(A.isEmpty || B.isEmpty) {
Future { Ok("Params missing") }
} else {
val futureResponse = for {
token <- getFromCache(A) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with param or cache", e))
}
wsResponse <- webServiceCall(B) recoverWith {
case e: Exception => Future.failed(new Exception("Some issue with web service call", e))
}
} yield Ok(wsResponse.body)
futureResponse recover {
case e: Exception => Ok(failureBody(e.getMessage))
}
}
对于人们如何更优雅地处理我的上述解决方案的任何评论,我将不胜感激。
正如您所注意到的,有很多方法可以处理依赖 Future
和 Option
的事情,这真的是一个品味问题。如果 if
表达式在给定情况下可以更清楚地传达您的观点,我认为使用 if
表达式并没有什么本质上的错误。那就是说你可以:
使用带有 .getOrElse
的 for
表达式来确保您的两个参数都得到满足(尽管嵌套的 for
有时看起来不太好)。稍微(不必要地)重写您的逻辑可能如下所示:
import scala.concurrent.Future.{successful => immediate}
def getFromCache(key: String, data: String): Future[String] = ???
def webServiceCall(key: String): Future[WSResponse] = ???
def myAction = Action.async { implicit request =>
(for {
a <- request.getQueryString("a")
b <- request.getQueryString("b")
} yield (for {
cachedData <- getFromCache(a)
r <- webServiceCall(b, cachedData)
} yield Ok(r.body)) recover {
case e => InternalServerError(e.getMessage)
}) getOrElse {
immediate(BadRequest("params missing"))
}
}
您还可以在 Play 中选择使用 Form
来确保多个参数存在且有效,因此您最终可能会得到类似以下的内容(再次略微压缩,根据需要展开):
import play.api.data.Forms._
import play.api.data.Form
val form = Form(tuple("a" -> nonEmptyText, "b" -> nonEmptyText))
def myAction2 = Action.async { implicit request =>
form.bindFromRequest.fold(
err => immediate(BadRequest("missing params")), { case (a, b) =>
getFromCache(a).flatMap { cachedData =>
webServiceCall(b, cachedData).map(r => Ok(r.body))
} recover {
case e => InternalServerError(e.getMessage)
}
}
)
}
关于这类事情的一些好帖子是:
- Error Handling in Scala
- How to compose Future and Option in Scala(在这种情况下可能有点多,但仍然很有趣。)