Akka(32): Http:High-Level-Api,Route exception handling

来源:http://www.cnblogs.com/tiger-xc/archive/2017/10/26/7735377.html
-Advertisement-
Play Games

Akka-http routing DSL在Route運算中拋出的異常是由內向外浮出的:當內層Route未能捕獲異常時,外一層Route會接著嘗試捕捉,依次向外擴展。Akka-http提供了ExceptionHandler類來處理Route運算產生的異常: 簡單來說ExceptionHandler類 ...


  Akka-http routing DSL在Route運算中拋出的異常是由內向外浮出的:當內層Route未能捕獲異常時,外一層Route會接著嘗試捕捉,依次向外擴展。Akka-http提供了ExceptionHandler類來處理Route運算產生的異常:

trait ExceptionHandler extends ExceptionHandler.PF {

  /**
   * Creates a new [[ExceptionHandler]] which uses the given one as fallback for this one.
   */
  def withFallback(that: ExceptionHandler): ExceptionHandler

  /**
   * "Seals" this handler by attaching a default handler as fallback if necessary.
   */
  def seal(settings: RoutingSettings): ExceptionHandler
}

object ExceptionHandler {
  type PF = PartialFunction[Throwable, Route]
  private[http] val ErrorMessageTemplate: String = {
    "Error during processing of request: '{}'. Completing with {} response. " +
      "To change default exception handling behavior, provide a custom ExceptionHandler."
  }

  implicit def apply(pf: PF): ExceptionHandler = apply(knownToBeSealed = false)(pf)

  private def apply(knownToBeSealed: Boolean)(pf: PF): ExceptionHandler =
    new ExceptionHandler {
      def isDefinedAt(error: Throwable) = pf.isDefinedAt(error)
      def apply(error: Throwable) = pf(error)
      def withFallback(that: ExceptionHandler): ExceptionHandler =
        if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = false)(this orElse that) else this
      def seal(settings: RoutingSettings): ExceptionHandler =
        if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = true)(this orElse default(settings)) else this
    }

  def default(settings: RoutingSettings): ExceptionHandler =
    apply(knownToBeSealed = true) {
      case IllegalRequestException(info, status) ⇒ ctx ⇒ {
        ctx.log.warning("Illegal request: '{}'. Completing with {} response.", info.summary, status)
        ctx.complete((status, info.format(settings.verboseErrorMessages)))
      }
      case NonFatal(e) ⇒ ctx ⇒ {
        val message = Option(e.getMessage).getOrElse(s"${e.getClass.getName} (No error message supplied)")
        ctx.log.error(e, ErrorMessageTemplate, message, InternalServerError)
        ctx.complete(InternalServerError)
      }
    }

  /**
   * Creates a sealed ExceptionHandler from the given one. Returns the default handler if the given one
   * is `null`.
   */
  def seal(handler: ExceptionHandler)(implicit settings: RoutingSettings): ExceptionHandler =
    if (handler ne null) handler.seal(settings) else ExceptionHandler.default(settings)
}

簡單來說ExceptionHandler類型就是一種PartialFunction:

trait ExceptionHandler extends PartialFunction[Throwable, Route]

因為ExceptionHandler就是PartialFunction,所以我們可以用case XXException來捕獲需要處理的異常。留下未捕獲的異常向外層Route浮出。當未處理異常到達最外層Route時統一由最頂層的handler處理。與RejectionHandler一樣,最頂層的handler是通過Route.seal設置的:

/**
   * "Seals" a route by wrapping it with default exception handling and rejection conversion.
   *
   * A sealed route has these properties:
   *  - The result of the route will always be a complete response, i.e. the result of the future is a
   *    ``Success(RouteResult.Complete(response))``, never a failed future and never a rejected route. These
   *    will be already be handled using the implicitly given [[RejectionHandler]] and [[ExceptionHandler]] (or
   *    the default handlers if none are given or can be found implicitly).
   *  - Consequently, no route alternatives will be tried that were combined with this route
   *    using the ``~`` on routes or the [[Directive.|]] operator on directives.
   */
  def seal(route: Route)(implicit
    routingSettings: RoutingSettings,
                         parserSettings:   ParserSettings   = null,
                         rejectionHandler: RejectionHandler = RejectionHandler.default,
                         exceptionHandler: ExceptionHandler = null): Route = {
    import directives.ExecutionDirectives._
    // optimized as this is the root handler for all akka-http applications
    (handleExceptions(ExceptionHandler.seal(exceptionHandler)) & handleRejections(rejectionHandler.seal))
      .tapply(_ ⇒ route) // execute above directives eagerly, avoiding useless laziness of Directive.addByNameNullaryApply
  }

上面的exceptionHandler沒有預設值,看起來好像有可能有些異常在整個Route運算里都不會被捕獲。但實際上Akka-http提供了預設的handler ExceptionHandler.default:

  /**
   * Creates a sealed ExceptionHandler from the given one. Returns the default handler if the given one
   * is `null`.
   */
  def seal(handler: ExceptionHandler)(implicit settings: RoutingSettings): ExceptionHandler =
    if (handler ne null) handler.seal(settings) else ExceptionHandler.default(settings)

通過這個ExceptionHandler.seal函數設置了最頂層的exception handler。

我們可以通過下麵的方法來定製異常處理的方式:

自定義ExceptionHandler,然後:

1、把Exceptionhandler的隱式實例放在頂層Route的可視域內(implicit scope)

2、或者,直接調用handleExceptions,把自定義handler當作參數傳入,把Route結構中間某層及其所有內層包嵌在handleExceptions中,例如:

    val route: Route =
    get {
      pathSingleSlash {
        complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
      } ~
        path("ping") {
         handleExceptions(customExceptionHandler) {  
           onSuccess(Future.successful("ok"))
           complete("PONG!")
          }
        } ~
        path("crash") {
          sys.error("BOOM!")
        }
    }

第一種辦法是一種頂層對所有未捕獲異常統一處理的方式,第二種辦法可以限制處理區域針對某層以內的Route進行異常捕捉。

下麵是第一種辦法的使用示範:

object ExceptiontionHandlers {
  implicit def implicitExceptionHandler: ExceptionHandler =
    ExceptionHandler {
      case _: ArithmeticException =>
        extractUri { uri =>
          complete(HttpResponse(InternalServerError, entity = s"$uri: Bad numbers, bad result!!!"))
        }
    }
  def customExceptionHandler: ExceptionHandler =
    ExceptionHandler {
      case _: RuntimeException =>
        extractUri { uri =>
          complete(HttpResponse(InternalServerError, entity = s"$uri: Runtime exception!!!"))
        }
    }

}

第二種方式的使用示範如下:

  val route: Route =
    get {
      pathSingleSlash {
        complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
      } ~
        path("ping") {
            onSuccess(Future.successful("ok"))
            complete("PONG!")
        } ~
        handleExceptions(customExceptionHandler) {
          path("crash") {
            sys.error("BOOM!")
          }
        }
    }

下麵是本次討論中的示範源代碼:

import akka.actor._
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import akka.http.scaladsl.server.Directives._
import akka.stream._
import StatusCodes._
import scala.concurrent._

object ExceptiontionHandlers {
  implicit def implicitExceptionHandler: ExceptionHandler =
    ExceptionHandler {
      case _: ArithmeticException =>
        extractUri { uri =>
          complete(HttpResponse(InternalServerError, entity = s"$uri: Bad numbers, bad result!!!"))
        }
    }
  def customExceptionHandler: ExceptionHandler =
    ExceptionHandler {
      case _: RuntimeException =>
        extractUri { uri =>
          complete(HttpResponse(InternalServerError, entity = s"$uriRuntime exception!!!"))
        }
    }

}

object ExceptionHandlerDemo extends App {
  import ExceptiontionHandlers._

  implicit val httpSys = ActorSystem("httpSys")
  implicit val httpMat = ActorMaterializer()
  implicit val httpEc = httpSys.dispatcher

  val (port, host) = (8011,"localhost")

  val route: Route =
    get {
      pathSingleSlash {
        complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
      } ~
        path("ping") {
            onSuccess(Future.successful("ok"))
            complete("PONG!")
        } ~
        handleExceptions(customExceptionHandler) {
          path("crash") {
            sys.error("BOOM!")
          }
        }
    }

  val bindingFuture: Future[Http.ServerBinding] = Http().bindAndHandle(route,host,port)

  println(s"Server running at $host $port. Press any key to exit ...")

  scala.io.StdIn.readLine()

  bindingFuture.flatMap(_.unbind())
    .onComplete(_ => httpSys.terminate())

}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • fvyhg ...
  • 有很多朋友有的因為興趣,有的因為生計而走向了.Net中,有很多朋友想學,但是又不知道怎麼學,學什麼,怎麼系統的學,為此我以我微薄之力總結歸納寫了一篇.Net web開發技術棧,以此幫助那些想學,卻不知從何起的朋友。 本文整理了當前企業web開發中的管理系統,商城等系統的常用開發技術棧。 C#常見運算 ...
  • .net中常會用到動態載入DLL,而DLL中可能包含各種參數、方法、窗體,如何來調用動態載入這些參數、方法、窗體呢? 在C#中,我們要使用反射,首先要搞清楚以下命名空間中幾個類的關係: System.Reflection命名空間 (1) AppDomain:應用程式域,可以將其理解為一組程式集的邏輯 ...
  • 首先討論下,有多少實際工作經驗叫老程式員呢?我這裡定義5年吧,畢竟我才在公司開發了5年多點.(真真實實的開發了5年多,極少出差,一坐一天的開發.畢業前兩年沒從事開發,不算) 我寫的博客關於具體的技術,實現的詳細說明或代碼的方式寫出來的極少.因為我擅長的只是.net,桌面開發方向,新入行的程式員web ...
  • 首先給Grid添加BindingSource,類型為BindingForForm2。或者設置Grid的DataSource為IEnumerable<BindingForForm2>。 BindingForForm2類型如下。 public class BindingForForm2 { public ...
  • 今天複習到了ADO.NET,就把他們的知識梳理總結出來 ADO.NET 是一組向 .NET 程式員公開數據訪問服務的類。提供了對各種關係數據、XML 和應用程式數據的訪問。 所有的數據訪問類位於System.Data.dll中。System.Data包含了DataSet以及其他的支持類;System ...
  • 判斷是否是同一人的方法——equals() 不能直接用per1==per2,這不是對象內容的比較而是存放對象地址的值得比較 在Person類中提供一個比較的方法compare()返回boolean值 註意this關鍵字指的是當前對象。 並且在類的內部即使是私有的屬性,也不需要使用共有的方法來調用。 ...
  • 原文地址:https://codex.wordpress.org/Rewrite_API Rewrite API(重寫規則API) Description(描述) WordPress allows theme and plugin developers to programmatically spe ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...