One thing that IMHO is really cool with Play 2 is how simple the framework is and how easy it is to write highly productive abstractions on top of those provided by the framework. That allows to start coding simple applications without having to learn a lot of things and to smoothly scale to the development of more complex applications.
URL path and query string binders are those kind of scalable abstractions. The framework comes with a minimal set of common supported types (Int
, String
, etc.) enough to handle general use cases and you can plug your own binders allowing with low effort to handle cases more specific to your project.
For instance, we’ll see in this blog post how to bind business objects from the URL path. It means we’ll be able to define routes like /show/3
to call an action such as the following:
The article
parameter will automatically be retrieved using the id extracted from the URL path.
So, let’s say we’re writing an application selling articles defined by the following model:
We want to define the following route to show an article:
If we try to compile the above code we get the following error: No URL path binder found for type models.Article. Try to implement an implicit PathBindable for this type.. Well, let’s implement a PathBindable[Article]
. The PathBindable[A]
trait allows to build a value of type A
from a parameter extracted from the URL path. It has two abstract methods, bind
and unbind
, allowing to build a value from the path and build a path fragment from a value, respectively.
In our case, we want to retrieve an article from its id, which has type Long
, so we first need to retrieve the id value from the path parameter and then we can find the article. Since Play already defines a PathBindable[Long]
we can just reuse it. The following code shows how we can provide an implicit PathBindable[Artice]
provided there is an available implicit PathBindable[Long]
value in the scope:
The bind
implementation first tries to bind the article id using the PathBindable[Long]
value, then it tries to find the corresponding article in the database using the Article.findById
method returning an Option[Article]
. If the article exists it is returned wrapped in a Right
. Otherwise the value Left("Article not found")
is returned. The unbind
implementation simply unbinds the article’s id.
Now that we provide an implicit PathBindable[Article]
, we have to ask Play to import it. We can do that by appending it to the routesImport
sbt setting. For example, if we defined it in a binders
package object, we can just add the following setting to our Play project definition:
And we are done. If have an article with id 42 and hit the URL /show/42
, the corresponding article will be retrieved.
Now you might wonder what we get if we hit a URL where the article does not exist. In this case, Play returns a Bad Request HTTP result. If you want to change this behavior and, for example, redirect the user to the articles list, you can override the onBadRequest
method in your Play Global object:
You can find a runnable application based on this post on GitHub.