RequestContext.reject如何工作?

And*_*yuk 1 scala akka spray

看看喷雾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请求的原始人吗?

jru*_*lph 5

它是如何工作的:

  1. spray-can层HTTP连接actor向服务发送一个HttpRequest对象,并期望一个HttpResponse为它
  2. HttpService接收此消息并将原始发件人(即喷涂层连接参与者)保存为根"响应者"
  3. 它创建的RequestContext作为ActorRef反应需要被发送到该持有请求和响应.
  4. RequestContext上的基本操作就像complete向响应者发送消息以完成请求一样
  5. 此外,reject将消息发送给响应者.但是,由于原始响应者只是喷雾罐级别Actor的ActorRef而拒绝消息不是由喷雾罐处理的,因此路由层必须挂钩到消息处理中以使用RejectionHandler将拒绝转换为普通的HttpResponse.这可以通过例如使用RequestContext.withRouteResponseMapped返回新的RequestContext来完成,该新的RequestContext包含新的响应者,该响应者将旧的应答器应用于将该响应者接收的所有消息,然后将结果转发给原始响应者.
  6. 这种方式RequestContext.withRejectionHandling可以实现:它返回一个新的RequestContext,它包含一个新的包装响应器,它将给定的函数应用于拒绝消息,然后将结果转发给包装的响应者.
  7. 现在,你的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.~现在只对第一条路线的结果进行模式匹配的新实现,以查看它是否拒绝了请求,在这种情况下运行第二条路由.