当应该使用scala恢复时:

时间:2017-07-30 23:07:52

标签: scala playframework

我有一个从控制器到服务的简单的订单流回到控制器,我试图确保我在正确的地方使用未来的恢复,并且通常正确地覆盖异常。

控制器动作:

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

 ...

}

什么是恢复这些未来的正确方法?

1 个答案:

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