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")
      }
    }
  }
}