如何在Play框架2.4.6中拦截和更改Scala Akka中的异常

时间:2016-02-22 08:57:34

标签: scala playframework akka

Actor( OrgaActor )向数据库角色( DbActor )询问文档。 DbActor 并不总是提供文档,而是数据库错误异常。 OrgaActor 现在应该使用详细信息更改收到的异常(它实际上是组织查询,而不是通用数据库查询)。我很难从 DbActor 拦截异常,并在发送给控制器的结果中更改它( OrgaCtl )。 OrgaActor

的非工作示例代码
def receive = {
    case GetDocument(id: String) => {
      try {
        val answer = (dbActor ? DbActor.GetDocument("Orga", id)).mapTo[JsValue]
        play.Logger.debug("in answer")
        answer.pipeTo(sender())
      } catch {
        case e: Exception =>
          play.Logger.debug("in exception")
          val fhe = error.FhException(e, 404, error.Error.ORGA_LOAD, "error on load " + id)
          sender() ! akka.actor.Status.Failure(fhe)          
    }
}

DbActor GetDocument

case GetDocument(coll: String, id: String) =>
  try {
    val response = new FigureheadDb().get(coll, id)
    sender() ! response.jsonElementPayload()
  } catch {
    case e: Exception =>
      val fhe = error.FhException(e, 404, error.Error.FH_ID, "error on load " + id + " in coll " + coll)
      sender() ! akka.actor.Status.Failure(fhe)
      throw fhe
  }

永远不会显示 inException 调试消息,所以我猜 DbActor 中的 throw fhe 代码永远不会到达调用 OrgaActor 即可。 DbActor 绑定到 OrgaActor ,其中 val dbActor = context.actorOf(Props [DbActor],name =“db-actor”)。< / p>

问题是:如何拦截组织actor中数据库actor抛出的错误并将丰富的错误传递给全局错误处理程序?目前,全局错误处理程序始终会获取数据库actor错误。

基于已接受的答案的代码

dbActor

case GetDocument(coll: String, id: String, originalSender: ActorRef) =>
  try {
    val response = new FigureheadDb().get(coll, id)
    sender() ! GetDocumentSuccess(response.jsonElementPayload(), originalSender)
  } catch {
    case e: Exception =>
      val fhe = error.FhException(e, 404, error.Error.FH_ID, "error on load " + id + " in coll " + coll)
      sender() ! GetDocumentFailed(fhe, id, originalSender)
  }

OrgaActor

case GetDocument(id: String) => {
    val answer : Future[Any] = dbActor ? DbActor.GetDocument(coll, id, sender())
    answer.map {
      case success: DbActor.GetDocumentSuccess => 
        play.Logger.debug("in success")
        success.originalSender ! success.result.as[JsValue]
      case failure: DbActor.GetDocumentFailed => {
        play.Logger.debug("in failure")
        val fhe = error.FhException(failure.cause, 404, error.Error.ORGA_LOAD, "error on load " + failure.id)
        failure.originalSender ! akka.actor.Status.Failure(fhe)
      }
      case any => {
        play.Logger.error("Dead end")
      }
    }
}

对象DbActor

  case class GetDocument(coll: String, id: String,  originalSender: ActorRef)
  // http://stackoverflow.com/questions/35549414/how-to-intercept-and-change-exception-in-scala-akka-in-play-framework-2-4-6
  sealed trait GetDocumentResult
  case class GetDocumentSuccess(result: JsValue, originalSender: ActorRef) extends GetDocumentResult
  case class GetDocumentFailed(cause: Exception, id: String, originalSender: ActorRef) extends GetDocumentResult

1 个答案:

答案 0 :(得分:0)

在你的OrgaActor中,你试图捕捉一个永不抛出的异常。

以下代码引自您的问题,但我在answer中添加了类型注释。

try {
    val answer: Future[JsValue] = (dbActor ? DbActor.GetDocument("Orga", id)).mapTo[JsValue]
    play.Logger.debug("in answer")
    answer.pipeTo(sender())
  } catch { ... }

您可能希望第二行(val answer ....)抛出异常,但它不会抛出异常,但会返回Future,这可能会成功或失败。

使用answer.pipeTo(sender())将来的结果(可能是Status.Failure)发送给发件人()。

作为一种解决方案,您可以定义一个密封特征GetDocumentResult,其中两个案例类实现了特征:

// you already have this case class, I added the original sender,
// since you might need this information to know where to send the final result
case class GetDocument(coll: String, id: String, originalSender: ActorRef)

sealed trait GetDocumentResult
case class GetDocumentSuccess(result: JsValue, originalSender: ActorRef) extends GetDocumentResult
case class GetDocumentFailed(cause: Exception, id: String, originalSender: ActorRef) extends GetDocumentResult

使用此DbActor可以发送GetDocumentSuccessGetDocumentFailed作为对OrgaActor的回复。一旦OrgaActor收到响应,它就可以将最终结果(成功或后处理的失败消息)发送给原始发件人。