Big Picture
The spray-can HTTP Server and the spray-servlet connector servlet both provide an actor-level interface
that allows your application to respond to incoming HTTP requests by simply replying with an HttpResponse
:
import spray.http._
import HttpMethods._
class MyHttpService extends Actor {
def receive = {
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
sender() ! HttpResponse(entity = "PONG")
}
}
While it’d be perfectly possible to define a complete REST API service purely by pattern-matching against the incoming
HttpRequest
(maybe with the help of a few extractors in the way of Unfiltered) this approach becomes somewhat
unwieldy for larger services due to the amount of syntax “ceremony” required. Also, it doesn’t help in keeping your
service definition as DRY as you might like.
As an alternative spray-routing provides a flexible DSL for expressing your service behavior as a structure of
composable elements (called Directives) in a concise and readable way. At the top-level, as the result of the
runRoute
wrapper, the “route structure” produces an Actor.Receive
partial function that can be directly supplied
to your service actor.
The service definition from above for example, written using the routing DSL, would look like this:
import spray.routing._
class MyHttpService extends HttpServiceActor {
def receive = runRoute {
path("ping") {
get {
complete("PONG")
}
}
}
}
This very short example is certainly not the best for illustrating the savings in “ceremony” and improvements in conciseness and readability that spray-routing promises. The Longer Example might do a better job in this regard.
For learning how to work with the spray-routing DSL you should first understand the concept of Routes.
The HttpService
spray-routing makes all relevant parts of the routing DSL available through the HttpService trait, which you can
mix into your service actor or route test. The HttpService
trait defines only one abstract member:
def actorRefFactory: ActorRefFactory
which connects the routing DSL to your actor hierarchy. In order to have access to all HttpService
members in your
service actor you can either mix in the HttpService
trait and add this line to your actor class:
def actorRefFactory = context
or, alternatively, derive your service actor from HttpServiceActor
class, which already defines the connecting
def actorRefFactory = context
for you.
The runRoute Wrapper
Apart from all the predefined directives the HttpService
provides one important
thing, the runRoute
wrapper. This method connects your route structure to the enclosing actor by constructing an
Actor.Receive
partial function that you can directly use as the “behavior” function of your actor:
import spray.routing._
class MyHttpService extends HttpServiceActor {
def receive = runRoute {
path("ping") {
get {
complete("PONG")
}
}
}
}