Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Sunday, September 11, 2011

Server Side Validation with the Play! Framework

This post presents an example of server side validation using the Play! Framework.  A form is backed by flash-scoped objects and the validation is performed by an explicit method call on a Controller's validation object.

Although a rich Javascript toolkit like Dojo can validate forms, your app should also support server-side validation for two reasons

  1. It's available if there are Javascript errors on the page.  This is particularly important for pages composed of several loosely-related "widgets" where errors in one widget affect the others on the page.
  2. Security.  If a page is saved and the validation is stripped, the server should gracefully handle the hacked version.
There are a few ways to validate a form in the Play! Framework.  This post uses an explicit call to a validation object associated with a Controller.  Other examples will work with an annotated Controller call or annotated POJO.  See the validation documentation for the other examples.

Flash Scope

Flash scope is a hybrid of request scope and session scope.  Flash scope allows objects to be accessed outside of the traditional request scope, but isn't quite as "sticky" as session scope.  The flash-scoped object is preserved through forwarding, but is cleared out when new requests are made from the browser.

This example backs up a form with a set of flash-scoped variables.  This is to preserve the submitted contents in the case of a validation error.  For example, if a form with an input text with a value of "ABCD" is submitted to a Controller expecting only 3 characters, the redisplayed form should retain the ABCD value with error messages and highlights from the view layer.

The View

This section of a form sets a Dojo ValidationTextBox from a flash-scoped variable 'tutorial_title_tx'.  Notice that when the form is saved, it will submit the value -- possibly changed by the user -- using the name 'tutorial.tutorial_title_tx'.

View Code
A successful submission of the form will trigger the 'flash.success' block (and not the 'ifErrors' block).

Successful Submission
An invalid form with flag the offending control (the ValidationTextBox) with a highlighted <span> element.  Also, a detailed explanation is presented at the top of the page.

Invalid Form
Controller

The Controller code supporting this form is divided into two methods: showTutorial() and saveTutorial().  showTutorial() is invoked on a GET request to display the initial form.  The method is also called through forwarding when there is an error.

The showTutorial() method will retrieve an object from the database (if found).  On the first pass, the flash variable 'tutorial_title_tx' will not be set, so rendering proceeds by passing the @Entity 'tutorial' to the render() call after setting the flash variable to the value currently stored in the DB.

GET Form - showTutorial() Controller Method

If there were an error, then the flash.get() condition would be false and what was put on the flash scope from the preceding saveTutorial() call would be used.

There is special handling for the Public Flag which is displayed as a checkbox control.  The Tutorial @Entity has a field public_fl that is a Boolean.  The form will submit a checkbox named _public_fl whose type is a text value like "on" or null if the box is unchecked.

Saving

The saveTutorial() method first validates the arguments which are gathered using a POJO 'tutorial' and additional fields like 'topic_cd' and 'account_id'.  Two validations are applied: required and maxlength.  If the validation passes, entities coming from <select> controls are pulled in, attached to the form's value object, and saved.  Note that the form's value object contains both the tutorialId primary key and the Hibernate version field.

  If an error message were found, then the flash scoped variable is set which will be used.

POST Form - saveTutorial() Controller Method
As with the showTutorial() method, there is special handling around the Public Flag field.  This is because it's checkbox rendering doesn't submit a true/false value, but rather an "on" value only if the checkbox is set.

Design Notes

This form-handling code flattens out a nested @Entity object called Tutorial.  Tutorial contains simple types like 'Long tutorial_id' and 'String tutorial_title_tx'.  Play! can automatically assign these from the form.  Dependent entities like 'Account creator' or 'Topic topic' are flattened, transporting only encoded values from the form.  In the Controller, the actual entities are retrieved and hooked up to the Tutorial @Entity.  Then, the Tutorial will be eligible to be saved.  If you try to reconstitute the dependent entities (Account, Topic) from the form, you may run into problems with JPA object state.

Message Resources

This example uses some custom messages that override the default values of validation.required and validation.max.  They follow the Play! documentation example, but I used a C printf technique for displaying an integer value with the max() function: %d rather than %s.

validation.required='%s' cannot be blank
validation.max='%s' cannot exceed %d characters


For a great UI, you'll want to use client-side validation, catching errors as the user is entering the data.  This obviates the need for a big post-submission dissection of an error report.  However, server side validation should still be used to produce a fault tolerant and secure application for cases when an unexpected browser or hacked-based form is sent.
 

 

2 comments:

  1. very good article. Nevertheless I find the flash approach a little too verbose and redundant, besides you have the 4kb limitation (flash is saved to cookies)

    I usually just render back to the form page (in this case showTutorial) passing as parameter the already populated object.

    have a look at this sample application

    https://github.com/opensas/events/blob/5-actions/app/controllers/Application.java

    I just issue

    public static void form(Long id) {
    Event event;
    if (id==null) {
    event = new Event();
    } else {
    event = Event.findById(id);
    }
    render(event);
    }

    public static void save(@Valid Event event) {
    if (validation.hasErrors()) {
    render("@form", event);
    }
    event.save();
    flash.success("Data successfully updated");
    list();
    }

    With this approach you have a little inconsistency between the url and the page displayed. In case of error, you end up posting values to /save and it render the page /form...

    ReplyDelete
  2. Hi opensas,

    Thanks for the suggestion.

    Maybe an overloaded form(Event event) method would eliminate the inconsistency? I'll try this with an @Valid on the POJO and post another article.

    -Carl

    ReplyDelete