Директивы маршрутизации обрабатываются некорректно, несмотря на то, что они не вызывают отклонение
Не могли бы вы объяснить, почему незначительные изменения полностью нарушают мою маршрутизацию?
Мой маршрут довольно прост
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", потому что вы не перехватываете исключение внутри директивы.