Playframework Routes – Part 1, Basics

Play Routes – Part 1, Basics

Routes? What’s to explain?

Routes in Play are not a difficult concept to grasp, but as always, the devil is in the details. Let me show you how to get the most out of routes and how to tackle some of the typical problems in designing the routing scheme of a Play web application.

Defining routes

Before we dive into the details, let’s recap the basics.

A route is a mapping from a URL to a controller action. Routes are defined in a Play project’s routes file in the conf folder. You can only use one routes file and routes in submodules are currently not supported (but will be in the upcoming 2.1 release).

A route is defined by specifying the HTTP method (Play supports GET, PUT, POST, DELETE), the path, and a fully-qualified controller method (usually called an action). These three elements are separated by as much whitespace as you like.

# Show the index page
GET     /                 controllers.Application.index()
# Login
POST    /login            controllers.Application.login()

Routes are evaluated from top to bottom, i.e. first come, first served. This is important in your route design, because if you’re not careful, routes can override each other.

On thing that Play doesn’t handle well is routes that end with a slash. In the Unix/Mac file system, /Applications and /Applications/ would be considered equal. On the web, these could be different. Since this only creates confusion, you should choose one layout and stick to it. You can then redirect All the other URLs to your preferred format, either in the routes file or better in the Global object (see last section).

GET /users   controllers.Users.showAllUsers()
GET /users/  controllers.Default.redirectTo("/users")

Parameters

In a web application, some parts of the URL are dynamic in nature. The path of the URL indicates a resource, while the query String provides options for the resource (most of the time HTML form values). In Play, you can map parameters to both the path as well as the query string.

You define path parameters using a colon, e.g. :username or :id, and map them by name (either as-is or with a regular expression) to parameters in the controller action. The controller action is written in Scala-like syntax, i.e. parameters are defined with <name>: <Type>.

# Show a user
GET     /users/:username  controllers.Users.showUser(username: String)
# Show user picture in a certain format, e.g. /users/joe/image/114x114
GET     /users/:username/image/$width&lt;[0-9]+&gt;x$height&lt;[0-9]+&gt;  controllers.Users.userImage(username: String, width: Int, height: Int)
# Delete a user
DELETE  /users/:id        controllers.Users.deleteUser(id: Long)

When you want to them appear as query parameters, simply don’t put them in the path.

# /users/browse?sortBy=lastName&amp;sortDirection=asc
GET     /users/browse     controllers.Users.browse(sortBy: String, sortDirection: String)

Query parameters can have default values, so when they are not provided, that value will be passed to the action.

# Default values
GET  /users/browse controllers.Users.browse(sortBy: String ?= "lastName", sortDirection: String ?= "asc", page: Int ?= 1)
# Now you can call /users/browse or /users/browse?sortBy=firstName&amp;page=10

Parameters can also have fixed values. These are default values that cannot be changed at all and help at defining cleaner URLs.

# Fixed values
GET  /users/:username/profile-image    controllers.Users.userImage(username: String, width: Int = 64, height: Int = 64)

By default, each parameter is of type String. This means you can omit the type declaration when you deal with Strings, although I prefer to keep it explicit.

Naturally, you don’t want to convert each parameter from String to some other time in each action, so other value types are automatically converted by Play. Play 2.0.x supports the Scala numerical types Int and Long, 2.1 adds Double, Float, List (Scala and Java), and java.util.UUID.

As mentioned above, you have to be careful about the evaluation order of routes.

GET /users/:username  controllers.Users.showUser(username)
GET /users/browse     controllers.Users.browseUsers()

What would happen, when a user chooses the name browse? Right, the /users/browse route would not match anymore.

Reverse Routing

Every controller and every template automatically imports the reverse routes. A route maps an URL to an action with its parameters, so a reverse route does the opposite by mapping an action call with its parameters to the URL.

Taking our example from above, routes.Users.browse() would resolve to /users/browse, and routes.Users.showUser("joe") to /users/joe. This parameter binding part is especially interesting because it also ensures that fixed parameters are resolved correctly.

What’s especially great is that the routes are compiled, so whenever you change a controller action’s signature, your controllers and templates will no longer compile. So be sure to always use the reverse routes instead of hard-coded URLs.

You can use the reverse routes outside a controller (e.g. in the GlobalSettings) by using the routes subpackage, e.g. controllers.routes.Users.show(1234) or by importing controllers.routes first and then using routes.Users.show(1234).

Reverse routes are of type play.mvc.Call (Java) or play.api.mvc.Call (Scala) and are handled automatically by Templates and Controllers. If you want to generate URLs yourself, for example when sending a double-opt-in e-mail, use the route’s methods url() or absoluteURL(). The latter takes a boolean parameter that can be used to force https URLs.

JavaScript Reverse Routing

The JavaScript reverse router (JS router for short) is a reverse router that can be used to generate URLs and AJAX calls in JavaScript.

You create a JS router by creating an action that returns Routes.javascriptRouter(). The first parameter is the name of the variable that is made available in JavaScript to access the routes. The second parameter is a varargs list of routes you want to use (this means you don’t have all routes available automatically), in the format of routes.javascript.<Controller>.<action>.

public static Result jsRoutes() {
  response().setContentType("text/javascript");
  return ok(Routes.javascriptRouter("jsRoutes", routes.javascript.Users.delete()));
}

Just as in the normal reverse router, you can call url(), absoluteURL() and websocketURL() on the route. Additionally, there is the ajax() function from jQuery, allowing you to quickly create a safe AJAX call like this:

<script src="@routes.Application.jsRoutes()"; type="text/javascript"></script>
jsRoutes.controllers.Users.delete(542312).ajax({
  success: function(data) {
    alert("User deleted!");
  }
});

In Scala, you can configure your JS routes directly in the template thanks to the javascriptRouter view helper. It requires an implicit request header in scope.

Global routing

Play’s router is only one way to map URLs to controller actions. The Global settings object has a method onRouteRequest which is called each time an incoming request should be routed to an action. The default implementation obviously asks the Play router to resolve and call the appropriate action. If you need something more dynamic than the rather static routes file (e.g. generated from the database), this is the place to plug in your own router.

Summary

Congratulations, you have now learned the basics about routes. The next part will explain some advanced uses of routes. I will also set up a GitHub repo with all the shown code in action.

Reference

Posted in Programming Tagged with: , ,
One comment on “Playframework Routes – Part 1, Basics
  1. Adelar da Silva Queiróz says:

    Thanks. Very helpful.

Leave a Reply

Your email address will not be published. Required fields are marked *

*


2 + one =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>