Routing
Routing handles the HTTP specific details of incoming requests and outgoing responses. The main uses of routes are to:
- match HTTP requests and extract Scala values;
- convert Scala values to an HTTP response; and
- reversing a route to create a link to the route or a client that calls the route.
A Route describes how to convert an incoming request into Scala values, and how to turn Scala values in an outgoing response.
A Handler is a Route that also includes the code to convert the values parsed from the request into the values that are used to create the response. In other words, a Handler includes business logic.
A Handlers is a collection of Handler.
The Route Type
The Route type is fairly complex, though you can ignore this in most uses.
Route[P <: Tuple, Q <: Tuple, I <: Tuple, O <: Tuple, R]
The types have the following meanings:
Pis the type of values extracted from the request's path by the Path.Qis the type of query parameters extracted by the Path.Iis the type of all values extracted from the HTTP request.Ois the type of values to construct an HTTP request to thisRoute. This is often, but not always, the same asI.Ris the type of the value to construct an HTTP response.
Most of these types are tuples because they accumulate values extracted from smaller components of the HTTP request. This will become clearer in the examples below.
Constructing A Route
A Route is constructed from two components:
The idiomatic way to construct a Route is by calling the Route.apply method, passing a Request and Response.
Here is a small example.
val route =
Route(
Request.get(Path / "user" / Param.int),
Response.ok(Entity.text)
)
This route tells us two things:
- It will match an incoming GET request to a path like
/user/1234and extract the number as anInt. - It convert a
Stringto an OK response. The response will include thatStringas the response's entity, and it will have a content typetext/plain.
To actually use this route we need to add a handler. In this case a handler would be either a function with type Int => String or with type Int => IO[String]. Here's a very simple example using an Int => String handler.
route.handle(userId => s"You asked for user $userId")
Notice that adding a handler produces a value with a different type, a Handler.
For more details see the separate pages for Request, Response and Handler.
Reverse Routing
There are three forms of reverse routing:
- constructing a
Stringthat corresponds to the path matched by theRoute; - constructing a
Stringcorresponding to the path and query parameters matched by theRoute; - constructing a HTTP request that will be matched by the
Route.
Reverse Routing for Paths
Given a Route you can construct a String containing the path to that route using the pathTo method. This can be used, for example, to embed hyperlinks to routes in generated HTML. Here's an example.
We first create a Route.
val viewUser = Route(Request.get(Path / "user" / Param.int), Response.ok(Entity.text))
Now we can call pathTo to construct a path to that route, which we could embed in an HTML form or a hyperlink.
viewUser.pathTo(1234)
// res0: String = "/user/1234"
Note that we pass to pathTo the parameters for the Path component of the route.
If the route has no path parameters there is an overload with no parameters.
Here's an example with no parameters.
val users = Route(Request.get(Path / "users"), Response.ok(Entity.text))
Now we can call pathTo without any parameters.
users.pathTo
// res1: String = "/users"
If there is more than one parameter we must collect them in a tuple. The route below has two parameters.
val twoParams = Route(Request.get(Path / "user" / Param.int / Param.string), Response.ok(Entity.text))
Notice when we call pathTo we pass a Tuple2.
twoParams.pathTo((1234, "McBoopy"))
// res2: String = "/user/1234/McBoopy"
Reverse Routing for Paths and Queries
You can use the pathAndQueryTo method to construct a String contains both the path and query parameters to a Route.
Here's an example of a Route that extracts elements from both the path and the query parameters.
val searchUsers = Route(
Request.get(
Path / "users" / "search" / Param.string :? Query[Int]("start").and[Int]("stop")
),
Response.ok(Entity.text)
)
searchUsers.pathAndQueryTo("scala", (1, 10))
// res3: String = "/users/search/scala?start=1&stop=10"
Combining Routes
Two or more routes can be combined using the orElse method, creating Routes.
val routes = viewUser.orElse(users).orElse(twoParams)
A Route or Routes can also be combined with an Application using overloads of the orElse method, which produces an Application.