Could you please explain why minor modification completely breaks my routing ?
My routing is quite simple
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
It supports two unauthenticated calls login
and serverInfo
. All other requests must have sessionId inside.
What I describe below happens when client makes only one login request.
Presented code works for login request when I use version with myAuthorization { }
. But it doesn't work with extractSession(payload) { }
. myAuthorization
imiplicitly takes HttpEntity
as input.
What puzzles me the most is that directives under ifTrue2
stopped working even though they have not changed. In debugger I see that IfTrue2
is called twice as expected: with ("login", "login")
and ("login", "serverInfo")
parameters.
Why does it work differently ? What shall I do to fix it ?
About bug in your new code (which causes an exception):
The entity directive takes payload from original http-request (it ignores results of
hprovide
- because it obviously can't understand hlists and works only with input request), your new code takes it from result ofparseXmlRequest
. That's the only difference.Simply saying - now you're calling smthing like
instead of
So instead of
<Envelope><Headers>...</><Body><function><param1>...<param1>...</></></>
it passess<param1>...</>...
togetSession
and kills your SOAP-authorization (at least by loosing SOAP-headers and root tags) and i think throws an exception.If you want to 'change' input request - use
mapRequest
instead ofhprovide
About your problem with custom directives:
Spray
calculates alldirective
s, but only one appropriatecomplete
, so you should catch exceptions inside your custom directive (one broken directive in any place may kill your whole routing):Example:
Actually i have ran your code with these mocks:
and received results as expected. But these mocks:
will always (even for login command) give you "InternalServerError" because you're not catching an exception inside directive.