尽管没有调用拒绝,但路由指令处理不正确

时间:2014-12-01 14:20:27

标签: scala spray spray-dsl

您能否解释一下为什么小修改会完全破坏我的路由?

我的路由非常简单

val myRoutes =
  pathPrefix("MainService") {
    post {
      requestInstance {
        request =>
          XmlBody {
            (command, payload) =>
              ifTrue2(command, "login") {
                complete {
                  "Return something here"    
                }
              } ~
                ifTrue2(command, "serverInfo") {
                  complete {
                    "Return something here"
                  }
                } ~
                extractSession(payload) { // OLD VERSION WAS: myAuthorization {
                  session =>
                    complete {
                      "Return something here"
                    }
                }
          }
      }

// Where custom directives look like this

def myAuthorization = entity(as[NodeSeq]).flatMap[Session :: HNil](
  getSession(_) match {
    case Some(session) => provide(session)
    case None => reject(AuthorizationFailedRejection)
  }
)

def extractSession(xmlPayload: ⇒ NodeSeq): Directive1[Session] =
  getSession(xmlPayload) match {
    case Some(session) => provide(session)
    case None => reject(AuthorizationFailedRejection)
  }

def ifTrue2(cmd : String, target : String): Directive0 =
  new Directive0 {
    def happly(func: HNil ⇒ Route) = {
      if (cmd.equalsIgnoreCase(target))
        func(HNil)
      else
        reject
    }
  }

def XmlBody = entity(as[NodeSeq]).flatMap[String :: Node :: HNil](
  parseXmlRequest(_) match {
    case Some(result) => hprovide(result)
    case None => reject(BadXmlRejection("Bad XML body"))
  }
)

def parseXmlRequest(xmlData: NodeSeq): Option[String :: Node :: HNil] = // body omitted for simplicity
def getSession(xmlRequest: NodeSeq): Option[Session] = // body omitted for simplicity

它支持两个未经身份验证的调用loginserverInfo。所有其他请求必须在其中包含sessionId。

当客户端只有一个登录请求时,我会在下面介绍。

当我使用myAuthorization { }版本时,提供的代码适用于登录请求。但它不适用于extractSession(payload) { }myAuthorization明确地将HttpEntity作为输入。

最令我困惑的是ifTrue2下的指令即使没有改变也停止了工作。在调试器中,我看到IfTrue2按预期调用了两次:使用("login", "login")("login", "serverInfo")参数。

为什么它的工作方式不同?我该怎么做才能解决它?

1 个答案:

答案 0 :(得分:0)

关于新代码中的错误(导致异常):

entity指令从原始http请求中获取有效负载(它忽略了hprovide的结果 - 因为它显然无法理解hlists并且仅适用于输入请求),您的新代码需要它来自parseXmlRequest的结果。这是唯一的区别。

简单地说 - 现在你正在调用像

这样的东西
 getSession(parseXmlRequest(request).payload) //it's now

而不是

 getSession(request.as[NodeSeq]) //it was before

所以代替<Envelope><Headers>...</><Body><function><param1>...<param1>...</></></>它将<param1>...</>...传递给getSession并杀死你的SOAP授权(至少通过丢失SOAP标头和根标签),我认为会引发异常。< / p>

如果你想改变&#39;输入请求 - 使用mapRequest代替hprovide


关于自定义指令的问题:

Spray计算所有 directive,但只有一个适当的complete,因此您应该在自定义指令中捕获异常(在任何地方一个破坏的指令可能会杀死你的整个路由):

def extractSession(xmlPayload: ⇒ NodeSeq): Directive1[Session] =
  try {
    getSession(xmlPayload) match {
      case Some(session) => provide(session)
      case None => reject(AuthorizationFailedRejection)
    }
  } catch {
    case t => reject(t)
  }
}

示例:

实际上我已经用这些模拟运行了你的代码:

  def parseXmlRequest(xmlData: NodeSeq): Option[String :: Node :: HNil] = {Some(xmlData.text :: <None/> :: HNil)}

  def getSession(xmlRequest: NodeSeq): Option[Session] = { Some(Session())}

并按预期收到结果。但是这些嘲笑:

  def parseXmlRequest(xmlData: NodeSeq): Option[String :: Node :: HNil] = {Some(xmlData.text :: <None/> :: HNil)}

  def getSession(xmlRequest: NodeSeq): Option[Session] = { sys.error("Error")}

将始终(即使对于登录命令)给你&#34; InternalServerError&#34;因为你没有在指令中捕获异常。