此多任务操作的功能样式

时间:2016-03-01 13:41:42

标签: scala playframework playframework-2.0

我有一个Play框架Action需要按顺序完成3件事: -

  1. 从请求中获取两个参数(A,B)
  2. 如果两个参数都存在,则使用A从缓存中检索条目
  3. 如果条目存在于缓存中,则使用B
  4. 调用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新手)完成此操作的一个很好的功能方法。如果params不存在,那么我不想执行for理解,但返回包含错误消息的Future[Result]。目前,我能想到这样做的唯一方法是使用if测试如下,但我不禁怀疑可能有更纯粹的方法来实现这一目标。

        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))
        }
    }
    

    我非常感谢有关人们如何更优雅地处理我的上述解决方案的任何意见。

1 个答案:

答案 0 :(得分:2)

正如您已经注意到的那样,有很多方法可以使用依赖FutureOption来做事,这真的是一种品味问题。如果在特定情况下更清楚地传达您的观点,我不会认为使用if表达式存在任何内在错误。那说你可以:

for表达式与.getOrElse一起使用,以确保您的参数都满意(尽管嵌套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"))
  }
}

您还可以使用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)
      }
    }
  )
}

要阅读这类事情的几篇好帖子是: