EQUIP2 WebApp Form Handling

Chris Greenhalgh, 2007-01-29; last updated 2007-02-02

Introduction

This document describes form handling in J2EE and Spring, independent of EQUIP2.

General

The examples for this document are all configured in the 'forms' servlet, i.e.

J2EE request

The URL http://.../forms/form_abstract_controller_form.html maps direct to the JSP form_abstract_controller_form.jsp, which is a minimal HTML form, with a single text input called 'arg'.

The form action is to POST (by default, URL-encoded) to http://.../forms/form_abstract_controller.html.

This is mapped to an instance of the controller class equip2.webapptutorial.forms.FormAbstractController, which is configured to return view named 'forms/form_abstract_controller_ok', with the argument and answer passed via the model as 'arg' and 'answer'.

The FormAbstractController obtains the form value(s) directly from the J2EE HttpServletRequest:

    String arg = request.getParameter("arg");

This view is mapped to the JSP form_abstract_controller_ok.jsp which prints a simple confirmation of the values.

Spring request

The preferred way of handling forms and other requests is Spring is using more specialised classes from the abstract Controller hierarchy, to automate the mapping of request parameters to "command" Java objects.

Abstract Command Controller

The abstract Spring Controller class org.springframework.web.servlet.mvc.AbstractCommandController is a suitable starting point for non-form commands (although we'll use it with a simple form, here, for demonstration purposes).

The URL http://.../forms/abstract_command_controller_form.html maps direct to the JSP abstract_command_controller_form.jsp, which is an identical minimal HTML form, with a single text input called 'arg'.

The form action is to POST (by default, URL-encoded) to http://.../forms/abstract_command_controller.html.

This is mapped to an instance of the controller class equip2.webapptutorial.forms.MyAbstractCommandController, which is also configured to return view named 'forms/abstract_command_controller_ok', with the answer passed via the model as 'answer'.

The controller method getCommand is over-ridden to create an instance of the class equip2.webapptutorial.forms.FormCommandBean (a simple Java bean with the String-typed property 'arg'), as required by the handle() method:

The abstract command controller's own implementation of handleRequestInternal calls getCommand to create an instance of the command object, and fill in its property values from the request parameters. This object is then passed to the handle() method as the command reference:

protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors)
throws Exception
The request parameter(s) can now be accessed from command object in a type-safe manner, and the Spring framework also supports some standard type coercions and a validation (checking) framework. E.g.:
	FormCommandBean formCommand = (FormCommandBean)command;
String arg = formCommand.getArg();

This command object may also passed to the view as the model (request) attribute 'command' (by default; the name can be changed by configuration).

Simple Form Controller

The abstract Spring Controller class org.springframework.web.servlet.mvc.SimpleFormController is the preferred starting point for form handling.

For example, the URL http://.../forms/simple_form_controller.html maps to an instance of the controller class equip2.webapptutorial.forms.MySimpleFormController.

By default, a simple form controller only considers HTTP POSTs to be possible form submissions; a HTTP get simple returns the configured form view, as specified by property 'formView' (in this case 'forms/simple_form_controller_form', rendered by simple_form_controller_form.jsp).

As with the abstract command controller, above, the form is backed by a Java Bean, by default identified by the model name 'command'. In the simple form controller this is created by the method formBackingObject rather than getCommand. This form controller uses the same back class as above,  equip2.webapptutorial.forms.FormCommandBean (a simple Java bean with the String-typed property 'arg'),

The form JSP (simple_form_controller_form.jsp) has a submission action with the same URL (http://.../forms/simple_form_controller.html) but method 'post'. The form also makes use of the Spring Taglib to help with form error reporting, e.g. for the 'arg' input:

    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
  <spring:bind path="command.arg">
'arg': <input
type="text"
value="<c:out value="${status.value}"/>"
name="<c:out value="${status.expression}"/>">
<c:if test="${status.error}">
Error:
<c:forEach items="${status.errorMessages}" var="error">
<c:out value="${error}"/>
</c:forEach>
</c:if>
</spring:bind>

Here, 'command.arg' refers to the property 'arg' of the form backing object (default name 'command').

When the form is first requested (HTTP get) the values are empty. However, if a form is submitted but fails validation, then the old (failed) value will be retained and the error reported as well. For example, the following code snippet from onSubmit() shows an additional check being made on the argument length:

	FormCommandBean formCommand = (FormCommandBean)command;
String arg = formCommand.getArg();
// do further validation...
if (arg==null || arg.length()<3) {
// signal an error
errors.rejectValue("arg", "too-short", "Arg is too short (<3)");
return showForm(request, response, errors);
}

If the form can create a custom view as its result then the handling of a (so far validated) submission is done by  onSubmit (e.g.):

    protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors)
throws Exception

Otherwise doSubmitAction can be over-riden if no additional information has to be passed to the model or view (the success view will be returned with the command and any errors in the model):

    protected void doSubmitAction(Object command)
throws Exception

In some cases, successful form submission may best be followed by a redirect of the browser to a new URL; this can be done via the HttpServletResponse method sendRedirect(java.lang.String location), via a Spring named view with a name of the form 'redirect:...'. or via a JSP:redirect from the success view.

A note on Internationalisation and text

In general it is considered to be a *Bad thing* to compile into the Java files text that will be returned directly to the user, since this may need to be changed when the web site is tidied up, and would need to be different in different languages. In the simple form controller example, above, if the controller rejects the form then the details of the error come from the controller and are presented to the user. The recommended way of handling this - supported by the use of the BindException class ('errors' in the example) - is to use a Spring Message Resolver to map message identifiers (such as 'too-short', above) into text for display.

Message Resolver(s) must be specified for each Servlet, e.g. in forms-servlet.xml as used here, there is the entry:

   <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"><value>forms</value></property>
</bean>

This causes the Spring 'forms' servlet to try to resolve messages using a property bundle with base name 'forms', i.e. from the file WEB-INF/classes/forms.properties, which in this case contains:

too-short.command.arg=The argument was too short (must be at least 3 characters long)

The dot-separated key is made up of the message (error) name ('too-short' - from the call to rejectValue), optionally the object name (in this case 'command'), and optionally in the case of field-specific errors the property name (in this case 'arg', also from the call to rejectValue). If a message is not found for the error then the default message (if any) is used (in this case, 'Arg is too short (<3)' from the call to rejectValue).

File upload from a form

File upload is easist in Spring using Command Controllers rather than a simple abstract controller, i.e. using a backing object or command object. Some additional configuration in the controller is required to set up the support for reading a submitted form into a backing object property of type byte[]. The URL http://.../forms/file_upload_form_controller.html gives a form with a file-type input element. Note that the form method MUST be post, and the enctype must be "multipart/form-data".

File upload support is enabled by the controller overriding of initBinder:

    /** initialise file upload support */
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
throws ServletException
{
binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
}

File upload support also requires that a multipart resolver is configured in the corresponding forms-servlet.xml configuration:

	<!--  enable multipart handling using commons-fileupload.jar -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

The contents of the file is then directly available in the controller. In this case the Controller rejects unspecified or zero-length files.

Changes

2007-01-29
2007-02-01
2007-02-02