I want to create a complex directive in Akka HTTP where I can use the query /api/guitar?id=42
or the path /api/guitar/42
. I did use two directives and it worked. However I would like to use everything in one directive where I can benefit from the |
operator. So, I have this code:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
object ComplexDirectives {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("ComplexDirectives")
val complexRoute: Route = path("api" / "guitar") {
(path("" / IntNumber) | parameter('id.as[Int])) { (itemNumber: Int) =>
get {
println(s"I found the guitar $itemNumber")
complete(HttpEntity(
ContentTypes.`text/html(UTF-8)`,
s"""
|<html>
| <body>I found the guitar $itemNumber!</body>
|</html>
|""".stripMargin
))
}
}
}
println("http GET localhost:8080/api/guitar?id=43")
println("http GET localhost:8080/api/guitar/42")
Http().newServerAt("localhost", 8080).bindFlow(complexRoute)
}
}
when I use a HTTP GET command http GET localhost:8080/api/guitar?id=43
I get the guitar with id 43. But when I use the command http GET localhost:8080/api/guitar/43
I cannot get the guitar. I also changed the directive to be only (path(IntNumber) | parameter('id.as[Int]))
but does not work.
Your issue is that you are trying to concatenate paths. In order to do that you need to use pathPrefix
. Try to do:
val complexRoute: Route = pathPrefix("api" / "guitar") {
(path(IntNumber) | parameter('id.as[Int])) { (itemNumber: Int) =>
get {
println(s"I found the guitar $itemNumber")
complete(HttpEntity(
ContentTypes.`text/html(UTF-8)`,
s"""
|<html>
| <body>I found the guitar $itemNumber!</body>
|</html>
|""".stripMargin
))
}
}
}
Please note that when declaring such path, more paths might be valid. For example, http://localhost:8080/api/guitar/abc?id=43
is valid because it has a prefix of api/guitar
and a parameter id.
If you want to block those paths, you need to use pathEnd
or pathEndOrSingleSlash
in the following way:
(path(IntNumber) | (pathEndOrSingleSlash & parameter('id.as[Int])))
which will support both http://localhost:8080/api/guitar/?id=43
and http://localhost:8080/api/guitar?id=43
Or:
(path(IntNumber) | (pathEnd & parameter('id.as[Int])))
which will support only http://localhost:8080/api/guitar?id=43