§String Interpolating Routing DSL
Play provides a DSL for defining embedded routers called the String Interpolating Routing DSL, or sird for short. This DSL has many uses, including embedding a light weight Play server, providing custom or more advanced routing capabilities to a regular Play application, and mocking REST services for testing.
Sird is based on a string interpolated extractor object. Just as Scala supports interpolating parameters into strings for building strings (and any object for that matter), such as s"Hello $to"
, the same mechanism can also be used to extract parameters out of strings, for example in case statements.
The DSL lives in the play.api.routing.sird
package. Typically, you will want to import this package, as well as a few other packages:
import play.api.mvc._
import play.api.routing._
import play.api.routing.sird._
A simple example of its use is:
val router = Router.from {
case GET(p"/hello/$to") => Action {
Results.Ok(s"Hello $to")
}
}
In this case, the $to
parameter in the interpolated path pattern will extract a single path segment for use in the action. The GET
extractor extracts requests with the GET
method. It takes a RequestHeader
and extracts the same RequestHeader
parameter, it’s only used as a convenient filter. Other method extractors, including POST
, PUT
and DELETE
are also supported.
Like Play’s compiled router, sird supports matching multi path segment parameters, this is done by postfixing the parameter with *
:
val router = Router.from {
case GET(p"/assets/$file*") =>
Assets.versioned(path = "/public", file = file)
}
Regular expressions are also supported, by postfixing the parameter with a regular expression in angled brackets:
val router = Router.from {
case GET(p"/items/$id<[0-9]+>") => Action {
Results.Ok(s"Item $id")
}
}
Query parameters can also be extracted, using the ?
operator to do further extractions on the request, and using the q
extractor:
val router = Router.from {
case GET(p"/search" ? q"query=$query") => Action {
Results.Ok(s"Searching for $query")
}
}
While q
extracts a required query parameter as a String
, q_?
or q_o
if using Scala 2.10 extracts an optional query parameter as Option[String]
:
val router = Router.from {
case GET(p"/items" ? q_o"page=$page") => Action {
val thisPage = page.getOrElse("1")
Results.Ok(s"Showing page $thisPage")
}
}
Likewise, q_*
or q_s
can be used to extract a sequence of multi valued query parameters:
val router = Router.from {
case GET(p"/items" ? q_s"tag=$tags") => Action {
val allTags = tags.mkString(", ")
Results.Ok(s"Showing items tagged: $allTags")
}
}
Multiple query parameters can be extracted using the &
operator:
val router = Router.from {
case GET(p"/items" ? q_o"page=$page"
& q_o"per_page=$perPage") => Action {
val thisPage = page.getOrElse("1")
val pageLength = perPage.getOrElse("10")
Results.Ok(s"Showing page $thisPage of length $pageLength")
}
}
Since sird is just a regular extractor object (built by string interpolation), it can be combined with any other extractor object, including extracting its sub parameters even further. Sird provides some useful extractors for some of the most common types out of the box, namely int
, long
, float
, double
and bool
:
val router = Router.from {
case GET(p"/items/${int(id)}") => Action {
Results.Ok(s"Item $id")
}
}
In the above, id
is of type Int
. If the int
extractor failed to match, then of course, the whole pattern will fail to match.
Similarly, the same extractors can be used with query string parameters, including multi value and optional query parameters. In the case of optional or multi value query parameters, the match will fail if any of the values present can’t be bound to the type, but no parameters present doesn’t cause the match to fail:
val router = Router.from {
case GET(p"/items" ? q_o"page=${int(page)}") => Action {
val thePage = page.getOrElse(1)
Results.Ok(s"Items page $thePage")
}
}
To further the point that these are just regular extractor objects, you can see here that you can use all other features of a case
statement, including @
syntax and if statements:
val router = Router.from {
case rh @ GET(p"/items/${idString @ int(id)}" ?
q"price=${int(price)}")
if price > 200 =>
Action {
Results.Ok(s"Expensive item $id")
}
}
§Binding sird Router
Configuring an application to use a sird Router can be achieved in many ways, depending on use case:
§Embedding play
An example of embedding a play server with sird router can be found in Embedding Play section.
§Providing a DI router
A router can be provided to the application as detailed in Application Entry point and Providing a router:
class SirdAppLoader extends ApplicationLoader {
def load(context: Context) = {
new SirdComponents(context).application
}
}
class SirdComponents(context: Context) extends BuiltInComponentsFromContext(context) {
lazy val router = Router.from {
case GET(p"/hello/$to") => Action {
Ok(s"Hello $to")
}
}
}
§Overriding binding
A router can also be provided using e.g. GuiceApplicationBuilder in the application loader to override with custom router binding or module as detailed in Bindings and Modules
Next: Javascript routing