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 (
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:
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[A] trait allows to build a value of type
A from a parameter extracted from the URL path. It has two abstract methods,
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:
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.