여러 WS 호출을 하나의 결과로 결합할 때 오류 처리
-
11-12-2019 - |
문제
스칼라, 플레이, 웹서비스를 한꺼번에 배우고 있으니 양해해주세요.저는 날씨 웹 서비스와 Google의 지리 코드 및 장소 웹 서비스를 결합하는 작은 수집기 서비스를 설정했습니다.뭔가 작동하고 있지만 오류를 처리하는 적절한 방법이 약간 혼란스럽습니다.(게시물 마지막에 코드를 게시했습니다)
따라서 장소 API는 위도/경도를 사용하므로 지리코드 API를 사용하여 우편번호에서 위도/경도를 가져옵니다.지오코드 API 호출의 응답을 처리할 때 다음과 같은 메시지가 표시됩니다. (Option[String], Option[String])
(에서 개최 maybeLoc
발).확인하는 match 문 내부 maybeLoc
, 결국 그렇게 된다면 (None, None)
, 나는 돌아 간다 Promise()
왜냐면 내가 반품을 해야 하거든 Promise
플랫맵 호출에서.
이에 대해 두 가지 질문이 있습니다.
1.) flatMap 또는 map 호출 중 하나에서 추가 처리를 수행할 수 없는 경우를 처리하는 올바른 방법은 무엇입니까?약속을 반환해야 하는데, 약속을 비워야 합니다. Promise
내가 상환하러 갈 때 시간이 초과될 것입니다. 정말 나쁜 생각인 것 같습니다.
2.) 다음을 호출한다고 가정하는 것이 옳습니까? Promise()
그것을 상환하려고 할 때 항상 시간 초과되는 빈 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)
)
}
}
}
해결책
(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
우리는 flatMap을 마치 마치 Option
, None을 얻으면 아무것도 처리하지 않는 능력을 얻습니다.결국 우리는 Promise[Option[T]]
이것은 매우 좋습니다.오류를 처리하는 또 다른 좋은 방법은 동일한 방법으로 EtherT/EtherT를 사용하는 것입니다.
|@|
Applicative Builder입니다.2개의 옵션이 필요하며 Option((Int, Int))
양측이 그렇다면 Some
.한쪽 또는 양쪽이 모두 있는 경우 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 그룹에 언제든지 도움을 요청하세요.