Merge pull request #16326 from spray/w/16289-fix-handleExceptions
=htp #16289 in `handleException` also catch strict exceptions during route building or execution
This commit is contained in:
commit
849eda2337
3 changed files with 126 additions and 5 deletions
|
|
@ -134,4 +134,36 @@ class BasicRouteSpecs extends RoutingSpec {
|
||||||
expect(dynamicRoute, "xxxx")
|
expect(dynamicRoute, "xxxx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case object MyException extends RuntimeException
|
||||||
|
"Route sealing" should {
|
||||||
|
"catch route execution exceptions" in {
|
||||||
|
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
|
||||||
|
get { ctx ⇒
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
} ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"catch route building exceptions" in {
|
||||||
|
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
|
||||||
|
get {
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
} ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"convert all rejections to responses" in {
|
||||||
|
object MyRejection extends Rejection
|
||||||
|
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
|
||||||
|
get {
|
||||||
|
reject(MyRejection)
|
||||||
|
}
|
||||||
|
} ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.server
|
||||||
|
package directives
|
||||||
|
|
||||||
|
import akka.http.model.StatusCodes
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
class ExecutionDirectivesSpec extends RoutingSpec {
|
||||||
|
object MyException extends RuntimeException
|
||||||
|
val handler =
|
||||||
|
ExceptionHandler {
|
||||||
|
case MyException ⇒ complete(500, "Pling! Plong! Something went wrong!!!")
|
||||||
|
}
|
||||||
|
|
||||||
|
"The `handleExceptions` directive" should {
|
||||||
|
"handle an exception strictly thrown in the inner route with the supplied exception handler" in {
|
||||||
|
exceptionShouldBeHandled {
|
||||||
|
handleExceptions(handler) { ctx ⇒
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"handle an Future.failed RouteResult with the supplied exception handler" in {
|
||||||
|
exceptionShouldBeHandled {
|
||||||
|
handleExceptions(handler) { ctx ⇒
|
||||||
|
Future.failed(MyException)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"handle an eventually failed Future[RouteResult] with the supplied exception handler" in {
|
||||||
|
exceptionShouldBeHandled {
|
||||||
|
handleExceptions(handler) { ctx ⇒
|
||||||
|
Future {
|
||||||
|
Thread.sleep(100)
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"handle an exception happening during route building" in {
|
||||||
|
exceptionShouldBeHandled {
|
||||||
|
get {
|
||||||
|
handleExceptions(handler) {
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"not interfere with alternative routes" in {
|
||||||
|
Get("/abc") ~>
|
||||||
|
get {
|
||||||
|
handleExceptions(handler)(reject) ~ { ctx ⇒
|
||||||
|
throw MyException
|
||||||
|
}
|
||||||
|
} ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[String] shouldEqual "There was an internal server error."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"not handle other exceptions" in {
|
||||||
|
Get("/abc") ~>
|
||||||
|
get {
|
||||||
|
handleExceptions(handler) {
|
||||||
|
throw new RuntimeException
|
||||||
|
}
|
||||||
|
} ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[String] shouldEqual "There was an internal server error."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def exceptionShouldBeHandled(route: Route) =
|
||||||
|
Get("/abc") ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[String] shouldEqual "Pling! Plong! Something went wrong!!!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,9 @@ package directives
|
||||||
import akka.http.util.FastFuture
|
import akka.http.util.FastFuture
|
||||||
import FastFuture._
|
import FastFuture._
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
trait ExecutionDirectives {
|
trait ExecutionDirectives {
|
||||||
import BasicDirectives._
|
import BasicDirectives._
|
||||||
|
|
||||||
|
|
@ -16,11 +19,15 @@ trait ExecutionDirectives {
|
||||||
* [[akka.http.server.ExceptionHandler]].
|
* [[akka.http.server.ExceptionHandler]].
|
||||||
*/
|
*/
|
||||||
def handleExceptions(handler: ExceptionHandler): Directive0 =
|
def handleExceptions(handler: ExceptionHandler): Directive0 =
|
||||||
extractRequestContext flatMap { ctx ⇒
|
Directive { innerRouteBuilder ⇒
|
||||||
import ctx.executionContext
|
ctx ⇒
|
||||||
mapRouteResultFuture {
|
import ctx.executionContext
|
||||||
_.fast.recoverWith(handler andThen (_(ctx.withContentNegotiationDisabled)))
|
def handleException: PartialFunction[Throwable, Future[RouteResult]] =
|
||||||
}
|
handler andThen (_(ctx.withContentNegotiationDisabled))
|
||||||
|
try innerRouteBuilder(())(ctx).fast.recoverWith(handleException)
|
||||||
|
catch {
|
||||||
|
case NonFatal(e) ⇒ handleException.applyOrElse[Throwable, Future[RouteResult]](e, throw _)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue