3 min read

Binding and validating objects using custom binders

Read the Play documentation about binding and validating objects. As validation is extremely important in any application, it basically has to fulfill several tasks.

First, it should not allow the user to enter wrong data. After a user has filled a form, he should get a positive or negative feedback, irrespective of whether the entered content was valid or not. The same goes for storing data. Before storing data you should make sure that storing it does not pose any future problems as now the model and the view layer should make sure that only valid data is stored or shown in the application. The perfect place to put such a validation is the controller.

As a HTTP request basically is composed of a list of keys and values, the web framework needs to have a certain logic to create real objects out of this argument to make sure the application developer does not have to do this tedious task.

You can find the source code of this example in the chapter2/binder directory.

How to do it…

Create or reuse a class you want created from an item as shown in the following code snippet:

public class OrderItem {

@Required public String itemId;
public Boolean hazardous;
public Boolean bulk;
public Boolean toxic;
public Integer piecesIncluded;

public String toString() {
return MessageFormat.format("{0}/{1}/{2}/{3}/{4}", itemId,
piecesIncluded, bulk, toxic, hazardous);
}
}

Create an appropriate form snippet for the index.xml template:

#{form @Application.createOrder()}
 <input type="text" name="item" /><br />
 <input type="submit" value="Create Order">
 #{/form}

Create the controller:

public static void createOrder(@Valid OrderItem item) {
 if (validation.hasErrors()) {
 render("@index");
 }

renderText(item.toString());
}

Create the type binder doing this magic:

@Global
 public class OrderItemBinder implements TypeBinder<OrderItem> {

@Override
public Object bind(String name, Annotation[] annotations, String
value,
Class actualClass) throws Exception {

OrderItem item = new OrderItem();
List<String> identifier = Arrays.asList(value.split("-", 3));

if (identifier.size() >= 3) {
item.piecesIncluded = Integer.parseInt(identifier.get(2));
}

if (identifier.size() >= 2) {
int c = Integer.parseInt(identifier.get(1));
item.bulk = (c & 4) == 4;
item.hazardous = (c & 2) == 2;
item.toxic = (c & 1) == 1;
}

if (identifier.size() >= 1) { item.itemId = identifier.get(0);
}

return item;
}
}

How it works…

With the exception of the binder definition all of the preceding code has been seen earlier. By working with the Play samples you already got to know how to handle objects as arguments in controllers. This specific example creates a complete object out of a simple String. By naming the string in the form value (<input …name=”item” />) the same as the controller argument name (createOrder(@Valid OrderItem item)) and using the controller argument class type in the OrderItemBinder definition (OrderItemBinder implements TypeBinder<OrderItem>), the mapping is done.

The binder splits the string by a hyphen, uses the first value for item ID, the last for piìesIncluded, and checks certain bits in order to set some Boolean properties.

By using curl you can verify the behavior very easily as shown:

curl -v -X POST --data "item=Foo-3-5" localhost:9000/order

Foo/5/false/true/true

Here Foo resembles the item ID, 5 is the piecesIncluded property, and 3 is the argument means that the first two bits are set and so the hazardous and toxic properties are set, while bulk is not.

There’s more…

The TypeBinder feature has been introduced in Play 1.1 and is documented at http://www.playframework.org/documentation/1.2/controllers#custombinding.

Using type binders on objects

Currently, it is only possible to create objects out of one single string with a TypeBinder. If you want to create one object out of several submitted form values you will have to create your own plugin for this as workaround. You can check more about this at:

http://groups.google.com/group/play-framework/browse_thread/thread/62e7fbeac2c9e42d

Be careful with JPA using model classes

As soon as you try to use model classes with a type binder you will stumble upon strange behavior, as your objects will always only have null or default values when freshly instanced. The JPA plugin already uses a binding and overwrites every binding you are doing.

LEAVE A REPLY

Please enter your comment!
Please enter your name here