spray-routing
The spray-routing module provides a high-level, very flexible routing DSL for elegantly defining RESTful web services. Normally you would use it either on top of a spray-can HTTP Server or inside of a servlet container together with spray-servlet.
Minimal Example
This is a complete, very basic spray-routing application:
import spray.routing.SimpleRoutingApp
object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
startServer(interface = "localhost", port = 8080) {
path("hello") {
get {
complete {
<h1>Say hello to spray</h1>
}
}
}
}
}
It starts a spray-can HTTP Server on localhost and replies to GET requests to /hello with a simple response.
Longer Example
The following is a spray-routing route definition that tries to show off a few features. The resulting service does not really do anything useful but its definition should give you a feel for what an actual API definition with spray-routing will look like:
import scala.concurrent.duration.Duration
import spray.routing.HttpService
import spray.routing.authentication.BasicAuth
import spray.routing.directives.CachingDirectives._
import spray.httpx.encoding._
trait LongerService extends HttpService with MyApp {
val simpleCache = routeCache(maxCapacity = 1000, timeToIdle = Duration("30 min"))
val route = {
path("orders") {
authenticate(BasicAuth(realm = "admin area")) { user =>
get {
cache(simpleCache) {
encodeResponse(Deflate) {
complete {
// marshal custom object with in-scope marshaller
getOrdersFromDB
}
}
}
} ~
post {
// decompresses the request with Gzip or Deflate when required
decompressRequest() {
// unmarshal with in-scope unmarshaller
entity(as[Order]) { order =>
// transfer to newly spawned actor
detach() {
complete {
// ... write order to DB
"Order received"
}
}
}
}
}
}
} ~
// extract URI path element as Int
pathPrefix("order" / IntNumber) { orderId =>
pathEnd {
// method tunneling via query param
(put | parameter('method ! "put")) {
// form extraction from multipart or www-url-encoded forms
formFields('email, 'total.as[Money]).as(Order) { order =>
complete {
// complete with serialized Future result
(myDbActor ? Update(order)).mapTo[TransactionResult]
}
}
} ~
get {
// JSONP support
jsonpWithParameter("callback") {
// use in-scope marshaller to create completer function
produce(instanceOf[Order]) { completer => ctx =>
processOrderRequest(orderId, completer)
}
}
}
} ~
path("items") {
get {
// parameters to case class extraction
parameters('size.as[Int], 'color ?, 'dangerous ? "no")
.as(OrderItem) { orderItem =>
// ... route using case class instance created from
// required and optional query parameters
}
}
}
} ~
pathPrefix("documentation") {
// cache responses to GET requests
cache(simpleCache) {
// optionally compresses the response with Gzip or Deflate
// if the client accepts compressed responses
compressResponse() {
// serve up static content from a JAR resource
getFromResourceDirectory("docs")
}
}
} ~
path("oldApi" / Rest) { pathRest =>
redirect("http://oldapi.example.com/" + pathRest, StatusCodes.MovedPermanently)
}
}
}