Akka(32): Http:High-Level-Api,Route exception handling
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 thisdef 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)(implicitroutingSettings: 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.dispatcherval (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())}
转载于:https://www.cnblogs.com/tiger-xc/p/7735377.html
总结
以上是生活随笔为你收集整理的Akka(32): Http:High-Level-Api,Route exception handling的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Cocos 2d-X Lua 游戏添加苹
- 下一篇: CentOS-文件操作