Spring provides a couple of out-of-the-box solutions for JSP and
JSTL views. Using JSP or JSTL is done using a normal view resolver defined
in the WebApplicationContext
. Furthermore,
of course you need to write some JSPs that will actually render the
view.
Just as with any other view technology you're integrating with
Spring, for JSPs you'll need a view resolver that will resolve your
views. The most commonly used view resolvers when developing with JSPs
are the InternalResourceViewResolver
and the
ResourceBundleViewResolver
. Both are declared in
the WebApplicationContext
:
<!-- the ResourceBundleViewResolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="basename" value="views"/> </bean> # And a sample properties file is uses (views.properties in WEB-INF/classes): welcome.class=org.springframework.web.servlet.view.JstlView welcome.url=/WEB-INF/jsp/welcome.jsp productList.class=org.springframework.web.servlet.view.JstlView productList.url=/WEB-INF/jsp/productlist.jsp
As you can see, the
ResourceBundleViewResolver
needs a properties
file defining the view names mapped to 1) a class and 2) a URL. With a
ResourceBundleViewResolver
you can mix different
types of views using only one resolver.
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
The InternalResourceBundleViewResolver
can
be configured for using JSPs as described above. As a best practice, we
strongly encourage placing your JSP files in a directory under the
'WEB-INF'
directory, so there can
be no direct access by clients.
When using the Java Standard Tag Library you must use a special
view class, the JstlView
, as JSTL needs some
preparation before things such as the i18N features will work.
Spring provides data binding of request parameters to command objects as described in earlier chapters. To facilitate the development of JSP pages in combination with those data binding features, Spring provides a few tags that make things even easier. All Spring tags have HTML escaping features to enable or disable escaping of characters.
The tag library descriptor (TLD) is included in the spring.jar
as well in the distribution
itself. Further information about the individual tags can be found in
the appendix entitled Appendix F, spring.tld.
As of version 2.0, Spring provides a comprehensive set of data binding-aware tags for handling form elements when using JSP and Spring Web MVC. Each tag provides support for the set of attributes of its corresponding HTML tag counterpart, making the tags familiar and intuitive to use. The tag-generated HTML is HTML 4.01/XHTML 1.0 compliant.
Unlike other form/input tag libraries, Spring's form tag library is integrated with Spring Web MVC, giving the tags access to the command object and reference data your controller deals with. As you will see in the following examples, the form tags make JSPs easier to develop, read and maintain.
Let's go through the form tags and look at an example of how each tag is used. We have included generated HTML snippets where certain tags require further commentary.
The form tag library comes bundled in
spring.jar
. The library descriptor is called
spring-form.tld
.
To use the tags from this library, add the following directive to the top of your JSP page:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
... where form
is the tag name prefix you
want to use for the tags from this library.
This tag renders an HTML 'form' tag and exposes a binding path
to inner tags for binding. It puts the command object in the
PageContext
so that the command object can be
accessed by inner tags. All the other tags in this library
are nested tags of the form
tag.
Let's assume we have a domain object called
User
. It is a JavaBean with properties such as
firstName
and lastName
. We will
use it as the form backing object of our form controller which returns
form.jsp
. Below is an example of what
form.jsp
would look like:
<form:form> <table> <tr> <td>First Name:</td> <td><form:input path="firstName" /></td> </tr> <tr> <td>Last Name:</td> <td><form:input path="lastName" /></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form:form>
The firstName
and lastName
values are retrieved from the command object placed in the
PageContext
by the page controller.
Keep reading to see more complex examples of how inner tags are used
with the form
tag.
The generated HTML looks like a standard form:
<form method="POST"> <table> <tr> <td>First Name:</td> <td><input name="firstName" type="text" value="Harry"/></td> </tr> <tr> <td>Last Name:</td> <td><input name="lastName" type="text" value="Potter"/></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form>
The preceding JSP assumes that the variable name of the form
backing object is 'command'
. If you have put the
form backing object into the model under another name (definitely a
best practice), then you can bind the form to the named variable like
so:
<form:form commandName="user"> <table> <tr> <td>First Name:</td> <td><form:input path="firstName" /></td> </tr> <tr> <td>Last Name:</td> <td><form:input path="lastName" /></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form:form>
This tag renders an HTML 'input' tag with type 'text' using the bound value. For an example of this tag, see Section 16.2.4.2, “The form tag”.
This tag renders an HTML 'input' tag with type 'checkbox'.
Let's assume our User
has preferences
such as newsletter subscription and a list of hobbies. Below is an
example of the Preferences
class:
public class Preferences { private boolean receiveNewsletter; private String[] interests; private String favouriteWord; public boolean isReceiveNewsletter() { return receiveNewsletter; } public void setReceiveNewsletter(boolean receiveNewsletter) { this.receiveNewsletter = receiveNewsletter; } public String[] getInterests() { return interests; } public void setInterests(String[] interests) { this.interests = interests; } public String getFavouriteWord() { return favouriteWord; } public void setFavouriteWord(String favouriteWord) { this.favouriteWord = favouriteWord; } }
The form.jsp
would look like:
<form:form> <table> <tr> <td>Subscribe to newsletter?:</td> <%-- Approach 1: Property is of type java.lang.Boolean --%> <td><form:checkbox path="preferences.receiveNewsletter"/></td> </tr> <tr> <td>Interests:</td> <td> <%-- Approach 2: Property is of an array or of type java.util.Collection --%> Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/> Herbology: <form:checkbox path="preferences.interests" value="Herbology"/> Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/> </td> </tr> <tr> <td>Favourite Word:</td> <td> <%-- Approach 3: Property is of type java.lang.Object --%> Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/> </td> </tr> </table> </form:form>
There are 3 approaches to the checkbox
tag
which should meet all your checkbox needs.
Approach One - When the bound value is of type
java.lang.Boolean
, the
input(checkbox)
is marked as 'checked' if the
bound value is true
. The
value
attribute corresponds to the resolved
value of the setValue(Object)
value
property.
Approach Two - When the bound value is of type
array
or
java.util.Collection
, the
input(checkbox)
is marked as 'checked' if the
configured setValue(Object)
value is present in
the bound Collection
.
Approach Three - For any other bound value type, the
input(checkbox)
is marked as 'checked' if the
configured setValue(Object)
is equal to the
bound value.
Note that regardless of the approach, the same HTML structure is generated. Below is an HTML snippet of some checkboxes:
<tr> <td>Interests:</td> <td> Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/> <input type="hidden" value="1" name="_preferences.interests"/> Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/> <input type="hidden" value="1" name="_preferences.interests"/> Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/> <input type="hidden" value="1" name="_preferences.interests"/> </td> </tr>
What you might not expect to see is the additional hidden field
after each checkbox. When a checkbox in an HTML page is
not checked, its value will not be sent to the
server as part of the HTTP request parameters once the form is
submitted, so we need a workaround for this quirk in HTML in order for
Spring form data binding to work. The checkbox
tag
follows the existing Spring convention of including a hidden parameter
prefixed by an underscore ("_") for each checkbox. By doing this, you
are effectively telling Spring that “
the checkbox was visible in the form and I want my object
to which the form data will be bound to reflect the state of the
checkbox no matter what
”.
This tag renders multiple HTML 'input' tags with type 'checkbox'.
Building on the example from the previous
checkbox
tag section. Sometimes you prefer not
to have to list all the possible hobbies in your JSP page. You would
rather provide a list at runtime of the available options and pass
that in to the tag. That is the purpose of the
checkboxes
tag. You pass in an
Array
, a List
or a
Map
containing the available options in the
"items" property. Typically the bound property is a collection so it
can hold multiple values selected by the user. Below is an example of
the JSP using this tag:
<form:form> <table> <tr> <td>Interests:</td> <td> <%-- Property is of an array or of type java.util.Collection --%> <form:checkboxes path="preferences.interests" items="${interestList}"/> </td> </tr> </table> </form:form>
This example assumes that the "interestList" is a
List
available as a model attribute containing
strings of the values to be selected from. In the case where you use a
Map, the map entry key will be used as the value and the map entry's
value will be used as the label to be displayed. You can also use a
custom object where you can provide the property names for the value
using "itemValue" and the label using "itemLabel".
This tag renders an HTML 'input' tag with type 'radio'.
A typical usage pattern will involve multiple tag instances bound to the same property but with different values.
<tr> <td>Sex:</td> <td>Male: <form:radiobutton path="sex" value="M"/> <br/> Female: <form:radiobutton path="sex" value="F"/> </td> </tr>
This tag renders multiple HTML 'input' tags with type 'radio'.
Just like the checkboxes
tag above, you
might want to pass in the available options as a runtime variable. For
this usage you would use the radiobuttons
tag.
You pass in an Array
, a
List
or a Map
containing
the available options in the "items" property. In the case where you
use a Map, the map entry key will be used as the value and the map
entry's value will be used as the label to be displayed. You can also
use a custom object where you can provide the property names for the
value using "itemValue" and the label using "itemLabel".
<tr> <td>Sex:</td> <td><form:radiobuttons path="sex" items="${sexOptions}"/></td> </tr>
This tag renders an HTML 'input' tag with type 'password' using the bound value.
<tr> <td>Password:</td> <td> <form:password path="password" /> </td> </tr>
Please note that by default, the password value is
not shown. If you do want the password value to
be shown, then set the value of the 'showPassword'
attribute to true, like so.
<tr> <td>Password:</td> <td> <form:password path="password" value="^76525bvHGq" showPassword="true" /> </td> </tr>
This tag renders an HTML 'select' element. It supports data
binding to the selected option as well as the use of nested
option
and options
tags.
Let's assume a User
has a list of
skills.
<tr> <td>Skills:</td> <td><form:select path="skills" items="${skills}"/></td> </tr>
If the User's
skill were in Herbology, the
HTML source of the 'Skills' row would look like:
<tr> <td>Skills:</td> <td><select name="skills" multiple="true"> <option value="Potions">Potions</option> <option value="Herbology" selected="selected">Herbology</option> <option value="Quidditch">Quidditch</option></select> </td> </tr>
This tag renders an HTML 'option'. It sets 'selected' as appropriate based on the bound value.
<tr> <td>House:</td> <td> <form:select path="house"> <form:option value="Gryffindor"/> <form:option value="Hufflepuff"/> <form:option value="Ravenclaw"/> <form:option value="Slytherin"/> </form:select> </td> </tr>
If the User's
house was in Gryffindor, the
HTML source of the 'House' row would look like:
<tr> <td>House:</td> <td> <select name="house"> <option value="Gryffindor" selected="selected">Gryffindor</option> <option value="Hufflepuff">Hufflepuff</option> <option value="Ravenclaw">Ravenclaw</option> <option value="Slytherin">Slytherin</option> </select> </td> </tr>
This tag renders a list of HTML 'option' tags. It sets the 'selected' attribute as appropriate based on the bound value.
<tr> <td>Country:</td> <td> <form:select path="country"> <form:option value="-" label="--Please Select"/> <form:options items="${countryList}" itemValue="code" itemLabel="name"/> </form:select> </td> </tr>
If the User
lived in the UK, the HTML
source of the 'Country' row would look like:
<tr> <td>Country:</td> <td> <select name="country"> <option value="-">--Please Select</option> <option value="AT">Austria</option> <option value="UK" selected="selected">United Kingdom</option> <option value="US">United States</option> </select> </td> </tr>
As the example shows, the combined usage of an
option
tag with the options
tag
generates the same standard HTML, but allows you to explicitly specify
a value in the JSP that is for display only (where it belongs) such as
the default string in the example: "-- Please Select".
The items
attribute is typically populated
with a collection or array of item objects.
itemValue
and itemLabel
simply
refer to bean properties of those item objects, if specified;
otherwise, the item objects themselves will be stringified.
Alternatively, you may specify a Map
of items, in
which case the map keys are interpreted as option values and the map
values correspond to option labels. If itemValue
and/or itemLabel
happen to be specified as well,
the item value property will apply to the map key and the item label
property will apply to the map value.
This tag renders an HTML 'textarea'.
<tr> <td>Notes:</td> <td><form:textarea path="notes" rows="3" cols="20" /></td> <td><form:errors path="notes" /></td> </tr>
This tag renders an HTML 'input' tag with type 'hidden' using
the bound value. To submit an unbound hidden value, use the HTML
input
tag with type 'hidden'.
<form:hidden path="house" />
If we choose to submit the 'house' value as a hidden one, the HTML would look like:
<input name="house" type="hidden" value="Gryffindor"/>
This tag renders field errors in an HTML 'span' tag. It provides access to the errors created in your controller or those that were created by any validators associated with your controller.
Let's assume we want to display all error messages for the
firstName
and lastName
fields
once we submit the form. We have a validator for instances of the
User
class called
UserValidator
.
public class UserValidator implements Validator { public boolean supports(Class candidate) { return User.class.isAssignableFrom(candidate); } public void validate(Object obj, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required."); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required."); } }
The form.jsp
would look like:
<form:form> <table> <tr> <td>First Name:</td> <td><form:input path="firstName" /></td> <%-- Show errors for firstName field --%> <td><form:errors path="firstName" /></td> </tr> <tr> <td>Last Name:</td> <td><form:input path="lastName" /></td> <%-- Show errors for lastName field --%> <td><form:errors path="lastName" /></td> </tr> <tr> <td colspan="3"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form:form>
If we submit a form with empty values in the
firstName
and lastName
fields,
this is what the HTML would look like:
<form method="POST"> <table> <tr> <td>First Name:</td> <td><input name="firstName" type="text" value=""/></td> <%-- Associated errors to firstName field displayed --%> <td><span name="firstName.errors">Field is required.</span></td> </tr> <tr> <td>Last Name:</td> <td><input name="lastName" type="text" value=""/></td> <%-- Associated errors to lastName field displayed --%> <td><span name="lastName.errors">Field is required.</span></td> </tr> <tr> <td colspan="3"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form>
What if we want to display the entire list of errors for a given
page? The example below shows that the errors
tag
also supports some basic wildcarding functionality.
path="*"
- displays all errors
path="lastName*"
- displays all errors
associated with the lastName
field
The example below will display a list of errors at the top of the page, followed by field-specific errors next to the fields:
<form:form> <form:errors path="*" cssClass="errorBox" /> <table> <tr> <td>First Name:</td> <td><form:input path="firstName" /></td> <td><form:errors path="firstName" /></td> </tr> <tr> <td>Last Name:</td> <td><form:input path="lastName" /></td> <td><form:errors path="lastName" /></td> </tr> <tr> <td colspan="3"> <input type="submit" value="Save Changes" /> </td> </tr> </table> </form:form>
The HTML would look like:
<form method="POST"> <span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span> <table> <tr> <td>First Name:</td> <td><input name="firstName" type="text" value=""/></td> <td><span name="firstName.errors">Field is required.</span></td> </tr> <tr> <td>Last Name:</td> <td><input name="lastName" type="text" value=""/></td> <td><span name="lastName.errors">Field is required.</span></td> </tr> <tr> <td colspan="3"> <input type="submit" value="Save Changes" /> </td> </tr> </form>
A key principle of REST is the use of the Uniform Interface.
This means that all resources (URLs) can be manipulated using the same
four HTTP methods: GET, PUT, POST, and DELETE. For each methods, the
HTTP specification defines the exact semantics. For instance, a GET
should always be a safe operation, meaning that is has no side
effects, and a PUT or DELETE should be idempotent, meaning that you
can repeat these operations over and over again, but the end result
should be the same. While HTTP defines these four methods, HTML only
supports two: GET and POST. Fortunately, there are two possible
workarounds: you can either use JavaScript to do your PUT or DELETE,
or simply do a POST with the 'real' method as an additional parameter
(modeled as a hidden input field in an HTML form). This latter trick
is what Spring's HiddenHttpMethodFilter
does.
This filter is a plain Servlet Filter and therefore it can be used in
combination with any web framework (not just Spring MVC). Simply add
this filter to your web.xml, and a POST with a hidden _method
parameter will be converted into the corresponding HTTP method
request.
To support HTTP method conversion the Spring MVC form tag was updated to support setting the HTTP method. For example, the following snippet taken from the updated Petclinic sample
<form:form method="delete"> <p class="submit"><input type="submit" value="Delete Pet"/></p> </form:form>
This will actually perform an HTTP POST, with the 'real' DELETE
method hidden behind a request parameter, to be picked up by the
HiddenHttpMethodFilter
, as defined in web.xml:
<filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>httpMethodFilter</filter-name> <servlet-name>petclinic</servlet-name> </filter-mapping>
The corresponding @Controller method is shown below:
@RequestMapping(method = RequestMethod.DELETE) public String deletePet(@PathVariable int ownerId, @PathVariable int petId) { this.clinic.deletePet(petId); return "redirect:/owners/" + ownerId; }