我有一个从控制器到服务的简单的订单流回到控制器,我试图确保我在正确的地方使用未来的恢复,并且通常正确地覆盖异常。
控制器动作:
def getSiblings(): Action[JsValue] = Action.async(parse.json) { request =>
request.body.validate[Person] match {
case JsSuccess(person, _) =>
peopleService.getSiblings(person).map(res => Ok(Json.toJson(res))) recover {
case t: Throwable =>
logger.error("error running getSiblings: ", t)
InternalServerError
}
case JsError(errors) => Future(BadRequest(s"not a good person format ${errors.mkString}"))
}
}
peopleService:
class PeopleService @Inject() extends LazyLogging {
def getSiblings(personToGetSiblings: Person): Future[List[SiblingResults]] = {
// isSibling is a method of a person that returnes a future and can fail
Future.sequence(listOfPeople.map(person => person.isSibling(personToGetSiblings))) recover {
case e: Exception => {
throw new RuntimeException("fail to get siblings with error: ", e)
}
}
}
}
case class SiblingResults (person: Option[Person])
和一个人:
@Singleton
class PersonA @Inject() (configuration: Configuration, peopleApi: PeopleApi) extends Person {
def isSibling(personToMatch: Person): Future[SiblingResults] = {
val res = for {
// areSiblings returnes a Future[Boolean]
areThey <- peopleApi.areSiblings(personToMatch, personInstance) recover {
case ex: Exception => throw new Exception("PeopleApi failed")
}
} yield areThey
if (res) Some(personInstance) else None
}
val personInstance = this
...
}
什么是恢复这些未来的正确方法?
答案 0 :(得分:0)
使用Play的动作合成来处理任何失败。这样你的代码就可以干净地处理业务逻辑而不需要额外的管道工作,例如异常处理等。你可以让异常冒泡到控制器,最后由ActionBuilder处理异常。
<强> ActionBuilder 强>
import play.api.libs.json.Json
import play.api.mvc.{ActionBuilder, Request, Result}
import play.api.mvc.Results.Status
import scala.concurrent.Future
/**
* Created by chlr on 12/2/16.
*/
object ErrRecoveryAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]) = {
block(request) recover errorHandler
}
def errorHandler: PartialFunction[Throwable, Result] = {
// you can apply a different statuscode other than 500 depending on the type of exception matched.
case th: Throwable =>
new Status(500).apply(Json.obj("error_message" -> th.getMessage, "error_class" -> th.getClass.getName))
}
}
控制器使用:
注意控制器中没有异常处理,而且其他服务类中不需要异常处理。
def getSiblings(): Action[JsValue] = ErrRecoveryAction.async(parse.json) {
request =>
peopleService
.getSiblings(request.body.as[Person])
.map(res => Ok(Json.toJson(res)))
}