【问题标题】:Handling errors when combining multiple WS calls into one result将多个 WS 调用合并为一个结果时处理错误
【发布时间】:2012-10-02 03:05:51
【问题描述】:

我正在同时学习 scala、play 和 Web 服务,所以请耐心等待。我已经建立了一个小聚合服务,它结合了天气网络服务和谷歌的地理编码和地点网络服务。我有一些工作,但我对处理错误的正确方法有点困惑。 (我把代码贴在文末)

所以地方 api 使用纬度/经度,所以我使用地理编码 api 从邮政编码中获取纬度/经度。在处理来自对地理编码 api 的调用的响应时,我最终得到一个 (Option[String], Option[String])(保存在 maybeLoc val 中)。在检查maybeLoc 的匹配语句中,如果它最终是(None, None),我会返回Promise(),因为我需要从flatmap 调用中返回Promise

对此我有两个问题:

1.) 在其中一个 flatMap 或 map 调用中无法进行任何进一步处理的情况下,正确的处理方法是什么?它要求我返回一个承诺,但创建一个空的Promise,当我去赎回时它会超时,这似乎是一个非常糟糕的主意。

2.) 我是否正确假设调用Promise() 会生成一个空的承诺对象,在尝试赎回它时总是会超时?我真的无法从 scaladoc 中分辨出来,也无法从 google 中找到任何关于它的信息。

我希望我的问题对你有意义并且足够清楚。代码如下:

def bothAsJson(zipcode:String) = Action {
    val promiseOfLoc = Geocode.buildUrlFor(zipcode).get()
    val promiseOfWeather = Weather.buildUrlFor(zipcode, "json").get()

    val result = promiseOfLoc.flatMap { locResp => 
        val maybeLoc = Geocode.extractLocation(locResp.body.toString())
        maybeLoc match {
            case (Some(lat), Some(lng)) => {
                val promiseOfPlaces = Places.buildUrlFor(lat,lng).get()
                promiseOfPlaces.flatMap { placesResp =>
                    promiseOfWeather.map { weatherResp =>
                        (weatherResp.body.toString(), placesResp.body.toString())
                    }
                }
            }
            case _ => Promise()
        }
    }

    Async {
        result.orTimeout("Timeout!", 2000).map {response =>
            response.fold(
                result => Ok("Got:\n\nweather:\n" + result._1 + "\n\nplaces:\n" + result._2),
                timeout => InternalServerError(timeout)
            )
        }
    }
}

【问题讨论】:

    标签: scala playframework-2.0


    【解决方案1】:

    如果你得到 (None, None) 你不应该期待超时,但我相信会返回另一条错误消息。我在下面提供了一个示例。

    我认为您需要来自 scalaz 7 的 OptionT。我会这样写:

    import scalaz._
    import Scalaz._
    
    def bothAsJson(zipcode:String) = Action {
        val promiseOfLoc = Geocode.buildUrlFor(zipcode).get.map { Option(_.body.toString()) }
        val promiseOfWeather = Weather.buildUrlFor(zipcode, "json").get
           .map{ lockResp => 
               val (lat,lng) = Geocode.extractLocation(locResp.body.toString())
               (lat |@| lng).tupled
           }
        def buildPlaces(lat: String, lng: String) = Places.buildUrlFor(lat,lng).get
           .map { Option(_.body.toString) }
    
        val result = (for {
           (lat, lng) <- OptionT(promiseOfLoc)
           places     <- OptionT(Places.buildUrlFor(lat,lng).get())
           weather    <- OptionT(promiseOfWeather)
        } yield (places, weather)).run
    
        Async {
            result.orTimeout("Timeout!", 2000).map {response =>
                response.fold(
                    result => {
                      result.map(
                       some => Ok("Got:\n\nweather:\n" + some._1 + "\n\nplaces:\n" + some._2)
                      ).getOrElse(BadRequest("lat/lng failed probably?"))
                    },
                    timeout => InternalServerError(timeout)
                )
            }
        }
    }
    

    使用OptionT,我们可以像Option 一样进行平面映射,如果我们得到None,就可以不处理任何东西。最后我们留下了一个Promise[Option[T]],这非常适合这个。另一种处理错误的好方法是使用 Either/EtherT 和相同的方法。

    |@| 是一个应用生成器。它需要 2 个选项,如果双方都是 Some,则返回 Option((Int, Int))。如果一侧或两侧都是None,则返回None

    请注意,要使其正常工作,您需要一个 scalaz Monad[Promise] 实例

    implicit val PromiseInstance = new Monad[Promise] {
      // override def map[A,B](fa: Promise[A])(f: A => B) = fa.map(f)
      def point[A](a: => A) = Promise.pure(a)
      def bind[A,B](fa: Promise[A])(f: A => Promise[B]) = fa.flatMap(f)
    }
    

    另外请注意,我已经在 SO 编辑器中编写了所有这些代码,可能缺少大括号。但是所有的代码应该或多或少都是对的,我在repl中测试了部分代码。

    请随时在 freenode irc 上的 #scalaz 或 scalaz google 群组上寻求帮助。

    【讨论】:

    • 感谢您的回复!我绝对不想仅仅因为其中一个调用发生错误而超时 -> 但 map 无论如何都要求我返回 Promise。查看您的示例,现在很清楚我应该让它返回Promise[Either/Option] 而不是Promise[String]。再次感谢 edit 错误,我的意思是 flatMap
    • flatMap 是 A => Promise[B],map 是 A => B。所以你根本不需要只用 map 返回 Promise。
    • btw OptionT(Places.buildUrlFor(lat,lng).get()) 应该是 OptionT(buildPlaces(lat,lng))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多