- Security. If a page is saved and the validation is stripped, the server should gracefully handle the hacked version.
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.
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'.
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.
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|
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.
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.