Play and SBT (again)
In your play application, you surely wrote a route file. It looks something like this:
GET / controllers.Application.index GET /hello/:name controllers.Application.sayHello(name: String) GET /assets/*file controllers.Assets.at(path="/public", file)
In play, and contrary to most scala web frameworks, the route file is not written in an internal scala DSL. This approach has a bunch of drawbacks: obviously it's not nicely scriptable as a Scala file would be, and it adds a bit of complexity to the framework. But since play is both a Java and Scala framework, it makes sense to use an external DSL (Java developers don't want to write Scala code). One could argue that Play should support Scala or Java for route definition, like it does for the rest of the API. It's probably possible, I don't think Java is expressive enough for the job thought.
In Play 1.x, actions were invoked dynamically. Each time a HTTP request hits the server, the application would figure out the correct action to call, and invoke it using reflection.
Play 2.x, focuses much more on typesafety. If the action definition and the route file don't match, we want the compiler to tell us. We want to detect non existing actions, type error etc. Putting it simply, we want the security provided by the Scala compiler for our routes.
This generator is composed of two parts:
Route file parser
If you're not familiar yet with parser combinator, this article, by Daniel Spiewak is a really nice intro.
The grammar of play route files is here, each element of syntax being represented by a
The output of this parser is a
If the route file is correctly defined, you get a
Success[List[Rule]], otherwise, a positionned Failure, with the error message. This way, Play can give you a nice error message if you made a mistake.
Alright, so far we've parsed the application route file, which gives us a list of
We now need to generate the corresponding Scala code.
Code generation functions are basically just mapping each
Rule to a String. It's nothing more than basic String manipulation. Let's have a look a the generated code for routing.
Once you've compile your project, the generated sources will be under
Play generated a router (
routes_routing.scala) and a reverse router file (
Rule defined in the route file, the
routes function tries to pattern match the incoming
RequestHeader. If a valid route is found (something matched),
invokeHandler simply calls the correct Action. It may use a QueryStringBindable, or a PathBindable to parse the parameters to the required types.
Each function in
ReverseApplication simply matches the names of the
This way you can simply call them directly, and you'll get a String. Since the parameters of those functions are the same as your Actions, the compiler will type check everything nicely.
Using the Reverse routing
Alright, so far we have generated functions invoking the Application actions based on RequestHeader, and reverseRouters, generating a String given the Action parameters.
Question is where is Play using that code?
For the reverse routers, the answer is obvious. You're almost calling them directly. For example if you write
routes.Application.sayHello("Julien") in a template, you're simply calling the
sayHello method in a
The reason you don't have to call ReverseApplication.sayHello, but Application.sayHello is because play generates a Java file mapping the reverse router classes names to the controller classes. This class is located under
target/scala-2.10/src_managed/controller and looks like this:
Using the router
Now last question is, when is the router called ? We've seen in the previous article that play delegates routing to the Global object. Let's have a look at the default global, which you normally either use directly, or extend when you define you own Global.
onRouteRequest method, calls
router.handlerFor(request). router is an Option in the current Application. handlerFor simply
PartialFunction[RequestHeader, Handler] generated by play to a
- Play uses the application routes file to generate scala sources of router an reverse routers
- Those sources are in
- reverse routing is (almost) directly called by the user
- the router is called in