看看喷雾API,它RequestContext是不可变的并RequestContext.reject返回Unit- 那么Spray路由如何知道"请求"被拒绝了?
http://spray.io/documentation/1.2.2/spray-routing/key-concepts/routes/
即:我们说我们有路线:a-> b
如果b拒绝请求(通过调用RequestContext.reject)如何a通知它?
我想我不确定"响应者"(参见文档中的响应者链)的文档意味着什么RequestContext.响应者b会是a吗?或者响应者是Actor发起http请求的原始人吗?
它是如何工作的:
complete向响应者发送消息以完成请求一样reject将消息发送给响应者.但是,由于原始响应者只是喷雾罐级别Actor的ActorRef而拒绝消息不是由喷雾罐处理的,因此路由层必须挂钩到消息处理中以使用RejectionHandler将拒绝转换为普通的HttpResponse.这可以通过例如使用RequestContext.withRouteResponseMapped返回新的RequestContext来完成,该新的RequestContext包含新的响应者,该响应者将旧的应答器应用于将该响应者接收的所有消息,然后将结果转发给原始响应者.RequestContext.withRejectionHandling可以实现:它返回一个新的RequestContext,它包含一个新的包装响应器,它将给定的函数应用于拒绝消息,然后将结果转发给包装的响应者.a ~ b工作实例如何(我明白你的意思是"a-> b")?如果你看一下它的实现~,它只是使用withRejectionHandling:.
def ~(otherRoute: Route): Route = { ctx ?
firstRoute {
ctx.withRejectionHandling { rejections ?
otherRoute(ctx.withRejectionsMapped(rejections ++ _))
}
}
}
Run Code Online (Sandbox Code Playgroud)
它传递给第一个route(a)一个RequestContext,它注册了一个Rejection处理程序,b如果第一个route()被拒绝,它将运行第二个route().然后将使用另一个RequestContext调用第二个路径,该RequestContext也是从原始路径派生的,在另一个拒绝的情况下将聚合来自两个路由的拒绝.
您还可以通过查看堆栈跟踪来查看正在发生的情况.对于这些路线定义
val a = (ctx: RequestContext) => ctx.reject()
val b = { (ctx: RequestContext) =>
Thread.dumpStack()
ctx.complete("hello world")
}
val demoRoute = a ~ b
Run Code Online (Sandbox Code Playgroud)
打印此堆栈跟踪(需要从下往上读取):
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1365)
// arrived at `b`
at spray.examples.DemoService$$anonfun$3.apply(DemoService.scala:42)
at spray.examples.DemoService$$anonfun$3.apply(DemoService.scala:41)
// `~` running the second route
at spray.routing.RouteConcatenation$RouteConcatenation$$anonfun$$tilde$1$$anonfun$apply$1.apply(RouteConcatenation.scala:32)
at spray.routing.RouteConcatenation$RouteConcatenation$$anonfun$$tilde$1$$anonfun$apply$1.apply(RouteConcatenation.scala:31)
// `RequestContext.withRejectionHandling` handling the Rejection
at spray.routing.RequestContext$$anonfun$withRejectionHandling$1.applyOrElse(RequestContext.scala:130)
at scala.runtime.AbstractPartialFunction$mcVL$sp.apply$mcVL$sp(AbstractPartialFunction.scala:33)
at scala.runtime.AbstractPartialFunction$mcVL$sp.apply(AbstractPartialFunction.scala:33)
at scala.runtime.AbstractPartialFunction$mcVL$sp.apply(AbstractPartialFunction.scala:25)
// `RequestContext.withRouteResponseHandling` doing its thing
at spray.routing.RequestContext$$anon$1.handle(RequestContext.scala:84)
at akka.spray.UnregisteredActorRefBase.$bang(UnregisteredActorRefBase.scala:72)
at spray.routing.RequestContext.reject(RequestContext.scala:202)
// `a` rejecting the request
at spray.examples.DemoService$$anonfun$2.apply(DemoService.scala:40)
at spray.examples.DemoService$$anonfun$2.apply(DemoService.scala:40)
// `~` running route `a`
at spray.routing.RouteConcatenation$RouteConcatenation$$anonfun$$tilde$1.apply(RouteConcatenation.scala:30)
at spray.routing.RouteConcatenation$RouteConcatenation$$anonfun$$tilde$1.apply(RouteConcatenation.scala:29)
// HttpService infrastructure
at spray.routing.directives.BasicDirectives$$anonfun$mapRequestContext$1$$anonfun$apply$1.apply(BasicDirectives.scala:30)
at spray.routing.directives.BasicDirectives$$anonfun$mapRequestContext$1$$anonfun$apply$1.apply(BasicDirectives.scala:30)
at spray.routing.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$4.apply(ExecutionDirectives.scala:35)
at spray.routing.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$4.apply(ExecutionDirectives.scala:33)
at spray.routing.HttpServiceBase$class.runSealedRoute$1(HttpService.scala:36)
at spray.routing.HttpServiceBase$$anonfun$runRoute$1.applyOrElse(HttpService.scala:46)
// HttpService receiving the request
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:425)
at akka.actor.ActorCell.invoke(ActorCell.scala:386)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:230)
at akka.dispatch.Mailbox.run(Mailbox.scala:212)
at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:506)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Run Code Online (Sandbox Code Playgroud)
作为旁注:大部分复杂性来自喷雾通过副作用完成响应,即通过向ActorRef发送消息.在即将推出的spray,akka-http的后续版本中,Route将简单地返回一个RouteResult,这使得控制流程更容易理解.例如,比较Route.~现在只对第一条路线的结果进行模式匹配的新实现,以查看它是否拒绝了请求,在这种情况下运行第二条路由.