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-)`,"<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-)`,"<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) = (,"localhost") val route: Route =
get {
pathSingleSlash {
complete(HttpEntity(ContentTypes.`text/html(UTF-)`,"<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()) }

Akka(32): Http:High-Level-Api,Route exception handling的更多相关文章

  1. Scalaz(32)- Free :lift - Monad生产线

    在前面的讨论里我们提到自由数据结构就是产生某种类型的最简化结构,比如:free monoid, free monad, free category等等.我们也证明了List[A]是个free mono ...

  2. python(32)- 模块练习Ⅱ:使用正则表达式实现计算器的功能

    开发一个简单的python计算器 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568 ...

  3. Sharded实现学习-我们到底能走多远系列(32)

    我们到底能走多远系列(32) 扯淡: 工作是容易的赚钱是困难的 恋爱是容易的成家是困难的 相爱是容易的相处是困难的 决定是容易的可是等待是困难的 主题: 1,Sharded的实现    Sharded ...

  4. Windows Phone开发(32):路径之PathGeometry

    原文:Windows Phone开发(32):路径之PathGeometry 说起路径这玩意儿,其实说的就是Path类,它藏在命名空间System.Windows.Shapes下,应该好找,它有一个很 ...

  5. Akka(2):Actor生命周期管理 - 监控和监视

    在开始讨论Akka中对Actor的生命周期管理前,我们先探讨一下所谓的Actor编程模式.对比起我们习惯的行令式(imperative)编程模式,Actor编程模式更接近现实中的应用场景和功能测试模式 ...

  6. Akka(6): become/unbecome:运算行为切换

    通过一段时间的学习了解,加深了一些对Akka的认识,特别是对于Akka在实际编程中的用途方面.我的想法,或者我希望利用Akka来达到的目的是这样的:作为传统方式编程的老兵,我们已经习惯了直线流程方式一 ...

  7. Akka(8): 分布式运算:Remoting-远程查找式

    Akka是一种消息驱动运算模式,它实现跨JVM程序运算的方式是通过能跨JVM的消息系统来调动分布在不同JVM上ActorSystem中的Actor进行运算,前题是Akka的地址系统可以支持跨JVM定位 ...

  8. Akka(33): Http:Marshalling,to Json

    Akka-http是一项系统集成工具.这主要依赖系统之间的数据交换功能.因为程序内数据表达形式与网上传输的数据格式是不相同的,所以需要对程序高级结构化的数据进行转换(marshalling or se ...

  9. Akka(0):聊聊对Akka的初步了解和想法

    前一段时间一直沉浸在函数式编程模式里,主要目的之一是掌握一套安全可靠的并发程序编程方法(concurrent programming),最终通过开源项目FunDA实现了单机多核CPU上程序的并行运算. ...

随机推荐

  1. 201521123108 《Java程序设计》第九周学习总结

    1. 本周学习总结 2. 书面作业 Q1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 答: 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? 答:经常出 ...

  2. 201521123015 《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...

  3. 201521123014 《Java程序设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 异常(Exception)处理 异常(Exception)的概念:在程序运行的时候可能出错,Java中把程序出现的错 ...

  4. Core Java 简单谈谈HashSet

    同学们在看这个问题的时候,我先提出者两个问题,然后大家带着问题看这个文章会理解的更好. HashSet为什么添加元素时不能添加重复元素? HashSet是否添加null元素? 打开源码, 我们看到如下 ...

  5. apache: eclipse的tomcatPluginV插件下载

    Sysdeo Eclipse Tomcat Launcher plugin Plugin features Support and contributions Download Installatio ...

  6. java通过JDBC链接SQLServer2012【转载!!!超详细】

    http://blog.csdn.net/stewen_001/article/details/19553173/

  7. [01] File类

    1.IO概念 File类是java.io包中一个很重要的类,这里的io,就是指 Input/Output,所以在看File类之前,先提一下所谓的IO概念. I/O(Input/Output),即输入/ ...

  8. Python shelve模块的使用方法

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = '人生入戏' import shelve,time #写 x = shelve. ...

  9. spring boot基础 入门

    spring boot基础 spring boot 的简单搭建 spring boot 的基本用法 spring boot 基本用法 自动配置 技术集成 性能监控 源码解析 工程的构建 创建一个mav ...

  10. Struts 2.5 Filter mapping specifies an unknown filter name [struts2]

    问题一:java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start ...