Play Framework: Binding and Validating Objects and Rendering JSON Output

Play Framework Cookbook


August 2011

$10.00

Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework

 

Play Framework Cookbook

Play Framework Cookbook

Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework

        Read more about this book      

(For more resources related to this subject, see here.)

Binding objects using custom binders

Read the Play documentation about object binding. 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.

        Read more about this book      

(For more resources related to this subject, see here.)

Validating objects using annotations

Basic validation should be clear for you now. It is well-documented in the official documentation and shows you how to use the different annotations such as @Min, @Max, @Url, @Email, @InFuture, @InPast, or @Range. You should go a step forward and add custom validation. An often needed requirement is to create some unique string used as identifier. The standard way to go is to create a UUID and use it. However, validation of the UUID should be pretty automatic and you want to be sure to have a valid UUID in your models.

You can find the source code of this example in the chapter2/annotation-validation directory.

Getting ready

As common practice is to develop your application in a test driven way, we will write an appropriate test as first code in this recipe. In case you need more information about writing and using tests in Play, you should read http://www.playframework.org/documentation/1.2/test.

This is the test that should work:

public class UuidTest extends FunctionalTest {

@Test
public void testThatValidUuidWorks() {
String uuid = UUID.randomUUID().toString();
Response response = GET("/" + uuid);
assertIsOk(response);
assertContentEquals(uuid + " is valid", response);
}

@Test
public void testThatInvalidUuidWorksNot() {
Response response = GET("/absolutely-No-UUID");
assertStatus(500, response);
}
}

So whenever a valid UUID is used in the URL, it should be returned in the body and whenever an invalid UUID has been used, it should return HTTP error 500.

How to do it...

Add an appropriate configuration line to your conf/routes file:

GET /{uuid} Application.showUuid

Create a simple @UUID annotation, practically in its own annotations or validations package:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(checkWith = UuidCheck.class)
public @interface Uuid {
String message() default "validation.invalid.uuid";
}

Create the appropriate controller, which uses the @Uuid annotation:

public class Application extends Controller {

public static void showUuid(@Uuid String uuid) {
if (validation.hasErrors()) {
flash.error("Fishy uuid");
error();
}

renderText(uuid + " is valid");
}
}

Create the check, which is triggered by the validation. You might want to put it into the checks package:

public class UuidCheck extends AbstractAnnotationCheck<Uuid> {

@Override
public booleanisSatisfied(Object validatedObject, Object value,
OValContext context, Validator validator)
throws OValException {

try {
UUID.fromString(value.toString());
return true;
} catch (IllegalArgumentException e) {}

return false;
}
}

How it works...

When starting your application via play test and going to http://localhost:9000/@ tests you should be able to run the UuidTest without problems.

Except the UuidCheck class, most of this here is old stuff. The Uuid annotation has two specialties. First it references the UuidCheck with a constraint annotation and second you can specify a message as argument. This message is used for internationalization.

The UuidCheck class is based on an Oval class. Oval is a Java library and used by the Play framework for most of the validation tasks and can be pretty easily extended as you can see here. All you need to implement is the isSatisfied() method. In this case it has tried to convert a String to a UUID. If it fails, the runtime exception thrown by the conversion is caught and false is returned, marking the check as invalid.

There's more...

The oval framework is pretty complex and the logic performed here barely scratches the surface. For more information about oval, check the main documentation at http://oval.sourceforge.net/.

Using the configure() method for setup

The AbstractAnnotationCheck class allows you to overwrite the configure(T object) method (where T is generic depending on your annotation). This allows you to set up missing annotation parameters with default data; for example, default values for translations. This is done by many of the already included Play framework checks as well.

Annotations can be used in models as well

Remember that the annotation created above may also be used in your models, so you can label any String as a UUID in order to store it in your database and to make sure it is valid when validating the whole object.

@Uuid public String registrationUuid;

Rendering JSON output

As soon as a web application consists of a very fast frontend, no or seldom complete page reloads occur. This implies a complete rendering by the browser, which is one of the most time consuming tasks when loading a webpage in the browser. This means you have to get the data as fast as possible to the client. You can either send them as pre-rendered HTML, XML, or JSON format. Sending the data in either JSON or XML means the application on the browser side can render the data itself and decide how and when it should be displayed. This means your application should be able to create JSON or XML-based responses.

As JSON is quite popular, this example will not only show you how to return the JSON representation of an entity, but also how to make sure sensitive data such as a password will not get sent to the user.

Furthermore some hypermedia content will be added to the response, like an URL where more information can be found.

You can find the source code of this example in the chapter2/json-render-properties directory.

Getting ready

Beginning with a test is always a good idea:

public class JsonRenderTest extends FunctionalTest {

@Test
public void testThatJsonRenderingWorks() {
Response response = GET("/user/1");
assertIsOk(response);

User user = new Gson().fromJson(getContent(response), User.
class);
assertNotNull(user);
assertNull(user.password);
assertNull(user.secrets);
assertEquals(user.login, "alex");
assertEquals(user.address.city, "Munich");
assertContentMatch("\"uri\":\"/user/1\"", response);
}
}

This expects a JSON reply from the request and parses it into a User instance with the help of gson, a JSON library from Google, which is also used by Play for serializing. As we want to make sure that no sensitive data is sent, there is a check for nullified values of password and secrets properties. The next check goes for a user property and for a nested property inside another object. The last check has to be done by just checking for an occurrence of the string, because the URL is not a property of the user entity and is dynamically added by the special JSON serializing routine used in this example.

How to do it...

Create your entities first. This example consists of a user, an address, and a SuperSecretData entity:

@Entity
public class User extends Model {

@SerializedName("userLogin")
public String login;
@NoJsonExport
public String password;
@ManyToOne
public Address address;
@OneToOne
public SuperSecretData secrets;

public String toString() {
return id + "/" + login;
}
}

@Entity
public class Address extends Model {
public String street;
public String city;
public String zip;
}

@Entity
public class SuperSecretData extends Model {
public String secret = "foo";
}

The controller is simple as well:

public static void showUser(Long id) {
User user = User.findById(id);
notFoundIfNull(user);
renderJSON(user, new UserSerializer());
}

The last and most important part is the serializer used in the controller above:

public class UserSerializer implements JsonSerializer<User> {

public JsonElement serialize(User user, Type type,
JsonSerializationContext context) {
Gsongson = new GsonBuilder()
.setExclusionStrategies(new LocalExclusionStrategy())
.create();

JsonElementelem = gson.toJsonTree(user);
elem.getAsJsonObject().addProperty("uri", createUri(user.id));
return elem;
}

private String createUri(Long id) {
Map<String,Object> map = new HashMap<String,Object>();
map.put("id", id);
return Router.reverse("Application.showUser", map).url;
}

public static class LocalExclusionStrategy implements
ExclusionStrategy {

public booleanshouldSkipClass(Class<?>clazz) {
return clazz == SuperSecretData.class;
}

public booleanshouldSkipField(FieldAttributes f) {
return f.getAnnotation(NoJsonExport.class) != null;
}
}
}

How it works...

The entities used in this example are simple. The only differences are the two annotations in the User entity. First there is a SerializedNamed annotation, which uses the annotation argument as field name in the json output – this annotation comes from the gson library. The @NoJsonExport annotation has been specifically created in this example to mark fields that should not be exported like a sensitive password field in this example. The address field is only used as an example to show how many-to-many relations are serialized in the JSON output.

As you might guess, the SuperSecretData class should mark the data as secret, so this field should not be exported as well. However, instead of using an annotation, the functions of the Google gson library will be utilized for this.

The controller call works like usual except that the renderJson() method gets a specific serializer class as argument to the object it should serialize.

The last class is the UserSerializer class, which is packed with features, although it is quite short. As the class implements the JsonSerializer class, it has to implement the serialize() method. Inside of this method a gson builder is created, and a specific exclusion strategy is added. After that the user object is automatically serialized by the gson object. Lastly another property is added. This property is the URI of the showUser() controller call, in this case something like /user/{id} . You can utilize the Play internal router to create the correct URL.

The last part of the serializer is the ExclusionStrategy, which is also a part of the gsonserializer. This strategy allows exclusion of certain types of fields. In this case the method shouldSkipClass() excludes every occurrence of the SuperSecretData class, where the method shouldSkipFields() excludes fields marked with the @NoJsonExport annotation.

There's more...

If you do not want to write your own JSON serializer you could also create a template ending with .json and write the necessary data like in a normal HTML template. However there is no automatic escaping, so you would have to take care of that yourself.

More about Google gson

Google gson is a pretty powerful JSON library. Once you get used to it and learn to utilize its features it may help you to keep your code clean. It has very good support for specific serializers and deserializers, so make sure you check out the documentation before trying to build something for yourself. Read more about it at http://code.google.com/p/google-gson/.

Alternatives to Google gson

Many developers do not like the gson library at all. There are several alternatives. There is a nice example of how to integrate FlexJSON. Check it out at http://www.lunatechresearch.com/archives/2011/04/20/play-framework-better-jsonserialization-flexjson.

Summary

In this article we took a look at binding objects using custom binders, validating objects using annotations, and rendering JSON output.


Further resources related to this subject:


Books to Consider

comments powered by Disqus