Play Framework
To use your routes in a project using the Play framework, add the following to your build.sbt
, choosing the
correct minor version of Play that you're using:
libraryDependencies += "bondlink" %% "routing-play_3.0" % "5.0.0"
The currently supported versions of Play are:
- 3.0.6 --
"bondlink" %% "routing-play_3.0" % "5.0.0"
- 2.9.6 --
"bondlink" %% "routing-play_2.9" % "5.0.0"
Play handlers will be of the shape Params => play.api.mvc.Handler
First let's rebuild our example routes.
import routing._
val Login = Method.GET / "login"
// Login: Route[GET, Unit] = /login
val Hello = Method.GET / "hello" / pathVar[String]("name")
// Hello: Route[GET, String] = /hello/<name: String>
val BlogPost = Method.GET / "post" / pathVar[String]("slug") :? queryParam[Int]("id")
// BlogPost: Route[GET, Tuple2[String, Int]] = /post/<slug: String>?<id: Int>
Then we can define our handlers.
import _root_.play.api.mvc.{ActionBuilder, AnyContent, Request, Results}
import routing.play._
import scala.concurrent.ExecutionContext.Implicits.global
val action = new ActionBuilder.IgnoringBody()
// action: IgnoringBody = play.api.mvc.ActionBuilder$IgnoringBody@503d6932
val handledLogin = Login.handle(_ => action(Results.Ok("Login page")))
// handledLogin: Handled[Action[AnyContent]] {
type M >: Method <: Method
type P >: Params <: Params
type R >: Login <: Login
} = routing.Route$$anon$3@526a8876
val handledHello = Hello.handle(name => action(Results.Ok(s"Hello, $name")))
// handledHello: Handled[Action[AnyContent]] {
type M >: Method <: Method
type P >: Params <: Params
type R >: Hello <: Hello
} = routing.Route$$anon$3@613164fb
val handledBlogPost = BlogPost.handle { case (slug, id) =>
action((req: Request[AnyContent]) => Results.Ok(s"Blog post with id: $id, slug: $slug found at ${req.uri}"))
}
// handledBlogPost: Handled[Action[AnyContent]] {
type M >: Method <: Method
type P >: Params <: Params
type R >: BlogPost <: BlogPost
} = routing.Route$$anon$3@7bf2859c
Handled routes can be composed into a play Router
by passing them to Route.router
:
import _root_.play.api.routing.Router
val router1: Router = Route.router(
handledLogin,
handledHello,
handledBlogPost
)
// router1: Router = play.api.routing.SimpleRouterImpl@44efddd9
If you prefer, you can call Router.from
with a partial function that matches on your Route
s manually:
val router2: Router = Router.from {
case Login(_) => action(Results.Ok("Login page"))
case Hello(name) => action(Results.Ok(s"Hello, $name"))
case BlogPost(slug, id) =>
action(req => Results.Ok(s"Blog post with id: $id, slug: $slug found at ${req.uri}"))
}
// router2: Router = play.api.routing.SimpleRouterImpl@5738311a
You can confirm that routes are matched correctly by passing some test requests to the router:
import org.apache.pekko.actor.ActorSystem
import _root_.play.api.libs.typedmap.TypedMap
import _root_.play.api.mvc.{EssentialAction, Headers, RequestHeader}
import _root_.play.api.mvc.request.{RemoteConnection, RequestTarget}
import scala.concurrent.Await
import scala.concurrent.duration._
implicit val actorSystem: ActorSystem = ActorSystem.create()
// actorSystem: ActorSystem = pekko://default
def fakeRequest(u: ReverseUri): RequestHeader =
new RequestHeader {
def attrs: TypedMap = TypedMap.empty
def connection: RemoteConnection = RemoteConnection("", false, None)
def headers: Headers = Headers()
def method: String = u.method.name
def target: RequestTarget =
RequestTarget(u.toString, u.path,
u.query.groupBy(_._1).map { case (k, v) => k -> v.flatMap(_._2) })
def version: String = ""
}
def testRoute(router: Router, call: Call): String = {
val request: RequestHeader = fakeRequest(call.uri)
val handler: EssentialAction = router.handlerFor(request).collect { case a: EssentialAction => a }.get
Await.result(handler(request).run().flatMap(_.body.consumeData).map(_.utf8String), 1.second)
}
testRoute(router1, Login())
// res0: String = "Login page"
testRoute(router1, Hello("world"))
// res1: String = "Hello, world"
testRoute(router1, BlogPost("my-slug", 1))
// res2: String = "Blog post with id: 1, slug: my-slug found at /post/my-slug?id=1"
testRoute(router2, Login())
// res3: String = "Login page"
testRoute(router2, Hello("world"))
// res4: String = "Hello, world"
testRoute(router2, BlogPost("my-slug", 1))
// res5: String = "Blog post with id: 1, slug: my-slug found at /post/my-slug?id=1"
You can also check that requests matching none of your routes are not handled by the router:
def unhandled(method: Method, path: String) =
router1.handlerFor(fakeRequest(ReverseUri(method, path, Vector())))
unhandled(Method.GET, "/fake")
// res6: Option[Handler] = None
// Not handled by `Hello` because the method doesn't match
unhandled(Method.POST, "/hello/world")
// res7: Option[Handler] = None