Директивы маршрутизации обрабатываются некорректно, несмотря на то, что они не вызывают отклонение

Не могли бы вы объяснить, почему незначительные изменения полностью нарушают мою маршрутизацию?

Мой маршрут довольно прост

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

Поддерживает два неаутентифицированных звонка login а также serverInfo, Все остальные запросы должны иметь sessionId внутри.

То, что я опишу ниже, происходит, когда клиент делает только один запрос на вход.

Представленный код работает для запроса входа в систему, когда я использую версию с myAuthorization { }, Но это не работает с extractSession(payload) { }, myAuthorization безоговорочно принимает HttpEntity в качестве ввода.

Что меня больше всего озадачивает, так это то, что директивы под ifTrue2 перестал работать, хотя они не изменились. В отладчике я вижу, что IfTrue2 вызывается дважды, как и ожидалось: с ("login", "login") а также ("login", "serverInfo") параметры.

Почему это работает по-другому? Что мне делать, чтобы это исправить?

1 ответ

Об ошибке в вашем новом коде (которая вызывает исключение):

Директива сущности берет полезную нагрузку из исходного http-запроса (игнорирует результаты hprovide - поскольку он, очевидно, не может понимать списки и работает только с запросом ввода), ваш новый код берет его из результата 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-заголовки и корневые теги), и я думаю, выдает исключение.

Если вы хотите "изменить" запрос ввода - используйте 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")}

всегда (даже для команды входа в систему) выдаст вам "InternalServerError", потому что вы не перехватываете исключение внутри директивы.

Другие вопросы по тегам