In the coming sections, we'll focus on how we can send data to the servers that is complex and is constrained in its shape and type. The first step being how to deal with a request for content.
Earlier we saw how to create a structure from the dictionary, which was created by us. However, in a web environment, such a dictionary will be coming from the client, for instance, the browser.
In this section, we'll see that Play! 2 comes with everything that we might need to map our external view back and forth to our server-side structure (until now User
).
To illustrate the simplest case, we'll create an HTML form that will mimic the dictionary:
This form was created directly from the previous template by changing its signature from a simple User
to an Option[User]
. This enables us to re-use the same template for two different cases:
Reviewing the form, we can note a new URL (/data/post
) that will handle our new request for a new action (post
), which we'll cover in a moment. The following screenshot shows its route definition:
So we have said that we want all URLs of the specified form to be routed to the post
action in the Data
controller.
Let's see what this new action does:
First of all, we see that we have used a new method on the form, bindFromRequest
, which is meant to retrieve the data needed by the form somewhere in the request.
Why somewhere? Actually, this method is able to look up data of several types and in several places. Depending on the content type, the HTTP method, and the encoding, this binding will look for data in:
In fact, the binding method will look everywhere to gather all the data and then it will retain the ones declared in the form definition to find which it's expecting.
After having found everything, it returns a Form
instance filled with these values on which we may call get
in order to retrieve the related domain object. But, we could also call the data
method that would give us back the dictionary of values.
Having reached this point, we can now provide our brand new user to the data
template rendered within an ok
(status 200) response.
Trying this in the browser, we'll get the following result:
That was really cool! From browser to domain object without any manual actions needed. I know what you are thinking, the use case is so simple that it doesn't reflect reality. So let's now test it a bit more by adding something new—validation of a complex structure.
In the real world, data is more complex and is required to satisfy some constraints for applications to accept it. In this section, we'll see how to deal with complexity and the next section will cover the constraints.
As Play! 2 is made for realistic applications, it already contains everything we might need to handle complexity.
For now, a user is nothing more than a name; but a real user (as more data is attached), has an age, a gender, an address, some friends, and so on. In brief, he/she can be represented as follows:
This should look like stuff we have already seen (possibly thousands of times). A significant change worth noting, however, is the public accessors on the class fields. This is due to the fact that Play! will generate them for us at compile time, so that they will be available for any other reflection-based tool.
The interesting things are that we have added one external indirection (address
) and two internal ones (friends
and spouse
). What would be really great is if we could bind everything from a single request! Ok, let's do some cool things now.
The really good thing at this stage, in Java, is that we don't have to touch anything in the server-side form definition, thankfully, due to the binder. Having said that, we can infer that the needed work will be on the client side only.
Earlier when creating our HTML form, we did it by hand mimicking the User
structure and hardcoded the action's URL. That's not what we'd expect from a framework like Play! 2, and it's true because the framework brings a lot of helpers to generate client-side code based on server-side information. So, rather than adapting the existing HTML form to match the new User
structure, we'll refactor it using Play! 2's features.
There are a lot of new concepts in this form. Conveniently, the interesting lines have been indexed using a comment and their review is as follows:
None
. Over here we can do the same on the value of the form—its value is actually None
until some data is used to fill it in, bound with valid data in an action.get
method of it in order to get the user back.helper
package provided by Play! 2. This one is defining plenty of utility functions (templates in fact) that are able to generate HTML code. In this case, we used the form template that generates an HTML form
tag. Passing it a route, it will be able to set the correct action and method attributes according to what is defined in the routes
file.input
tag of type
text. To generate it, it requires a form's field, which is retrieved by using the given form instance, using it as a function with the field name (userForm("name"))
.The routes
file is compiled in objects under the routes
object. Actually, all controllers will be available in this object, but reversed so that we're able to recover the mapping routes for a given pair controller-action. So in the template that we just discussed, it provides the form helper with the URL and the HTTP method defined for the post
action.
The @* *@
notation is simply comments in the Scala template's syntax.
So far, so good; we have changed the signature of the template that required us to change our action a bit. But, we'll also take the opportunity to change the HTTP method of the post
action to something more relevant for a modification of the server-side state: POST
.
We can be satisfied now by how simple our actions are. The show
method (renamed from test
) is just asking the template to be rendered using the userForm
instance, and the post
one does the same but asks for a new instance of the form bound to the request. That's all! We can now start editing and showing our user as the output of our form. Let's try this:
So, we retrieved what we had done earlier, but User
is now far more complex than a simple name. This means it is now time to update our form with all of the relevant information about the new fields.
Wow! A lot in a few minutes, isn't it?
In this new version of the template, we have defined almost a whole user; what hasn't been done yet are the links to other users. How did we achieve that? This was done thanks to the default helpers available in Play! 2.
Some save points have been set in the template, which means that it's time to review them one by one:
number
one. There is no inputNumber
template defined in Play! 2 (at the time of writing), however, it is very trivial to create our own using the generic input
template.More than just using the template, we've had to create the HTML block by ourselves, with the help of the given data. At this stage, you might be wondering about the worth of this, but we'll cover that when validation occurs.
checkbox
template to generate a checkbox.fieldset
tag.fieldset
tag, we defined the input text for fullStreet
and county
. It was trivial, but what is very cool is how we retrieved a form field using its path to the information from the user. Indeed, we can navigate the object's graph in the forms simply by using the dot notation. The only thing to ensure in this case is that every intermediate object is at least initialized to dummies (otherwise an NPE will be thrown).As the labels are ugly by default (they equal the name of the field), we used an argument of the input to set a custom label to something more readable.
country
, we wanted to present a list to the user. HTML has a specific form element for this case: select
. This element has a name and contains a list of options defined by a value and a display.Of course, Play! 2 enables us to define such HTML blocks really easily (which is a pain otherwise), all by passing a list of Tuple2
values where the first component is the value that will be sent with the form and the second component is the display one.
So now, what happens if we enter some data in the resulting form?
The first try was a real success, but what about the second one (on the right). A guy with no name, no age, and no home—the first piece of data is strange, the second one is frightening me, and the last one makes me sad. Let's try to take things one step further by adding validation.
What we're able to do now is create complex forms (both on the client and server sides) to represent our data, however, most of the time, they have to satisfy some constraints—business ones, for instance.
Java has a Java Specification Request (JSR) defined for exactly this situation, JSR 303, wherein how constraints can be added to Java models—using annotations—is specified.
So, Play! 2 takes advantages of this JSR and enables us to use it to validate our data, but it also defines custom validators that are missing in the specification.
What we'll find very convenient is that the validation information is available on the browser side as well, thanks to the form HTML helpers we saw earlier.
Considering our User
and Address
models, the following screenshot shows how we could ask Play! 2 to validate them:
Actually, we'll find everything we might need in the package play.api.validation
, especially the static methods available in the Constraints
class that defines the most common annotations. But, sometimes we might need ones from the JSR; for instance, in the User
model we imported Valid
from the javax.validation
package. Also we can see that while defining the constraints, a new field, email
, has appeared.
Browsing the code, we'll find almost all of the validation instructions that have been set to be trivial. Let's review them one by one:
name
field, which states that the field won't be valid if a null value is given.email
, we can see that it is also required but that it is also constrained to be an Email
annotation, which is simply asking for a validation against the classic e-mail pattern.Min
and Max
validation rules.Valid
one. This one is related to Required
but is for embedded structures. That is, if an external object has to be set and is also constrained by validation rules, we can ask the system to check its validity as part of the upper-level object validation process (it's a kind of a cascade on validation request).Address
, we use the Pattern
validation, which supersedes the Email
validation rule, letting us define our own pattern. That's exactly what has been done for the street, which is supposed to be fully qualified by containing the street and the number.message
property in an annotation. This message is what will be returned if this particular validation fails.select
element with options having their values set to two chars (IDs). So these IDs will now be validated by the MaxLength
constraint.This last rule might not be enough though, because someone could send a custom HTTP request with a fake country ID. To validate that the value is one of the predefined options, we can define our own logic in a dedicated method—validate
:
As shown in the previous screenshot, the validate
method is checking our custom rule and will return null
if it succeeds; an error message otherwise.
It seems that we've protected a lot of things on the server side now, but how are they presented (if they are) on the other side—in the HTML form?
Gosh! Without changing anything on the client side, the validation instructions have appeared on each field. Recalling what we wondered earlier, "What was the value of such helpers?" So, here we are; and it's not the end.
On the right side (previous screenshot) is presented a slightly skinned version of our HTML form in order to highlight where to look.
Ok, but how is it supposed to work when incorrect data is sent to the server?
Impressive, isn't it? Without changing a single line of code in our templates, every error just shows up. The only thing that has been done is to add a CSS rule to display the error messages in red.
Oh yeah! The red div
element is also a little addition. This addition takes the number of errors that are available in the form object directly, which is shown as follows:
Moreover, it's intuitive!