查看spray API,RequestContext
是不可变的,RequestContext.reject
返回Unit
- 所以Spray路由如何知道"请求"被拒绝了?
http://spray.io/documentation/1.2.2/spray-routing/key-concepts/routes/
I.e。:假设我们有路线:a-> b
如果b
拒绝该请求(通过致电RequestContext.reject
),a
如何通知?
我想我不确定"响应者" (请参阅RequestContext
的文档中的The Responder Chain)。 b
的响应者会a
吗?或者响应者是发起http请求的原始Actor
吗?
答案 0 :(得分:5)
它是如何工作的:
complete
只是向响应者发送消息以完成请求reject
,会向响应者发送一条消息。但是,由于原始响应者只是喷雾罐级别Actor的ActorRef,并且拒绝消息不是由喷雾罐处理的,因此路由层必须挂钩到消息处理中以使用RejectionHandler转换拒绝一个普通的HttpResponse。这可以通过例如完成。通过使用RequestContext.withRouteResponseMapped
返回一个新的RequestContext ,其中包含一个新的响应者,它将旧的应用程序应用到响应者收到的所有消息上,然后将结果转发给原始响应者。RequestContext.withRejectionHandling
:它返回一个新的RequestContext ,其中包含一个新的包装响应器,它将给定的函数应用于拒绝消息,然后将结果转发给包装的响应者。 / LI>
a ~ b
的例子是如何工作的(我理解你的意思是" a-> b")?如果您查看implementation of ~
,则只会使用withRejectionHandling
:
def ~(otherRoute: Route): Route = { ctx ⇒
firstRoute {
ctx.withRejectionHandling { rejections ⇒
otherRoute(ctx.withRejectionsMapped(rejections ++ _))
}
}
}
它传递给第一个路径(a
)一个RequestContext,它注册了一个拒绝处理程序,如果第一个路由被拒绝,它将运行第二个路由(b
)。然后将使用另一个RequestContext调用第二个路径,该RequestContext也是从原始路径派生的,在另一个拒绝的情况下,将聚合来自两个路由的拒绝。
您还可以通过查看堆栈跟踪来了解发生了什么。对于这些路线定义
val a = (ctx: RequestContext) => ctx.reject()
val b = { (ctx: RequestContext) =>
Thread.dumpStack()
ctx.complete("hello world")
}
val demoRoute = a ~ b
打印此堆栈跟踪(需要从下往上读取):
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)
作为旁注:大多数复杂性来自喷雾通过副作用完成响应,即通过向ActorRef发送消息。在即将推出的喷雾继承者中,akka-http,而不是Route will simply return a RouteResult,这使得控制流程更容易理解。例如。比较Route.~
的新实现,它现在只对第一条路线的结果进行模式匹配,看它是否拒绝了请求,在这种情况下运行第二条路线。
答案 1 :(得分:2)
虽然RequestContext
本身可能是不可变的,但它包含的值清楚地表明它会执行副作用。在不查看代码的情况下,我认为它与responder: ActorRef
值有关。在github上找到reject
的实现可能并不困难。
case class RequestContext(
request: HttpRequest,
responder: ActorRef,
unmatchedPath: Path) extends Product with Serializable
修改:对reject
的来电确实会向responder
发送消息(通过@ j-keck的评论)https://github.com/spray/spray/blob/1ce512cbd17380655fe1756b524c7f19dc9a3de3/spray-routing/src/main/scala/spray/routing/RequestContext.scala#L195
编辑:我刚刚注意到你问题的最后一部分。根据我在代码中看到的内容,可能在一些自定义指令中替换了响应者。我不知道这是否真的在实践中发生,抱歉。根据指令文档,原始responder
最终应该间接收到响应。我的猜测是,当Future
调用创建的?
完成后,原始请求就会关闭。如果未向原始?
中的responder
发送任何回复,则此RequestContext
的来电最终会超时。
http://spray.io/documentation/1.2.2/spray-routing/key-concepts/directives/#the-responder-chain
原始RequestContext的响应者,即发送者 HttpRequest的ActorRef接收响应并将其发送给 客户。