Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Composing Directives

Johannes Rudolph edited this page · 8 revisions

. . .

Deprecation Note

This documentation is for release 0.9.0 (from 03/2012), which is built against Scala 2.9.1 and Akka 1.3.1 (see Requirements for more information). Most likely, this is not the place you want to look for information. Please turn to the main spray site at http://spray.io for more information about other available versions.

. . .

As you have seen from the examples presented so far the "normal" way of composing directives is nesting. Let's take another look at the example from the Directives page:

val route: Route =
  path("order" / HexIntNumber) { id =>
    get {
      completeWith {
        "Received GET request for order " + id
      }
    } ~
    put {
      completeWith {
        "Received PUT request for order " + id
      }
    }
  }

Here the get and put directives are chained together with the ~ operator to form a higher-level route that serves as the inner route of the path directive. To make this structure more explicit you could also write the whole thing as such:

def innerRoute(id: Int): Route =
  get {
    completeWith {
      "Received GET request for order " + id
    }
  } ~
  put {
    completeWith {
      "Received PUT request for order " + id
    }
  }

val route: Route = path("order" / HexIntNumber) { id => innerRoute(id) }

What you can't see from this snippet is that directives are not implemented as simple methods but rather as stand-alone objects (of a SprayRoute subtype). This gives you more flexibility when composing directives. For example you can also use the | operator on directives. Here is yet another way to write the example:

val route =
  path("order" / HexIntNumber) { id =>
    (get | put) { ctx =>
      ctx.complete("Received " + ctx.request.method + " request for order " + id)
    }
  }

If you have a larger route structure where the (get | put) snippet appears several times you could also factor it out like this:

val getOrPut = get | put
val route =
  path("order" / HexIntNumber) { id =>
    getOrPut { ctx =>
      ctx.complete("Received " + ctx.request.method + " request for order " + id)
    }
  }

As an alternative to nesting you can also use the & operator:

val getOrPut = get | put
val route =
  (path("order" / HexIntNumber) & getOrPut) { id => ctx =>
    ctx.complete("Received " + ctx.request.method + " request for order " + id)
  }

And once again, you can factor things out if you want:

val orderGetOrPut = path("order" / HexIntNumber) & (get | put)
val route =
  orderGetOrPut { id => ctx =>
    ctx.complete("Received " + ctx.request.method + " request for order " + id)
  }

This type of combining directives with the | and & operators as well as "saving" more complex directive configurations as a val works across the board, with almost all directives taking inner routes. Thereby spray makes sure that all extractions work as expected and logical constraints are enforced at compile-time. For example you cannot | a directive producing an extraction with one that doesn't:

val route = path("order" / IntNumber) | get // doesn't compile

Also the extraction types have to match up:

val route = path("order" / IntNumber) | path("order" / DoubleNumber)   // doesn't compile
val route = path("order" / IntNumber) | parameter('order.as[Int])      // ok

When you combine directives producing extractions with the & operator all extractions will be properly gathered up:

val order = path("order" / IntNumber) & parameters('oem, 'expired ?)
val route =
  order { (orderId, oem, expired) =>
    ...
  }

Summing up, directives offer a great way of constructing your web service logic from small building blocks in a plug and play fashion while maintaining DRYness and full type-safety. If the large range of predefined directives does not fully satisfy your needs you can also very easily create Custom Directives.

Something went wrong with that request. Please try again.