11.2 Validating Complex Input
Without a Bean
Before we look at the two
remaining database sections, let's go back and take a look at
the two application pages we skipped earlier, namely the
enter.jsp and validate.jsp pages used for input
to the employee registration.
In Chapter
8, I introduced you to validation of user input using the
JSTL <c:if> action as well as using an
application-specific bean. The bean contains all validation
code and can therefore validate the format of complex data,
such as date strings, email addresses, and credit-card
numbers. This is the approach I recommend, but if you're
developing a JSP-based application without access to a Java
programmer to develop the beans you need, I'll show you a
trick you can use to validate dates and a custom action for
email-address validation.
The validate.jsp page uses the JSTL
<c:if> action and the custom actions to
validate all user input. If an input parameter isn't valid, an
error message is saved in a variable, and the request is
forwarded back to the enter.jsp page. The
enter.jsp page adds all the error messages to the
response, so to the user, the result is identical to the
bean-based validation approach you saw in Chapter
8.
Let's look at validate.jsp first, shown in Example
11-8.
Example 11-8. Validation
with application beans (validate.jsp) <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="ora" uri="orataglib" %>
<c:set var="isValid" value="true" />
<c:if test="${empty param.userName}">
<c:set var="userNameError" scope="request"
value="User Name missing" />
<c:set var="isValid" value="false" />
</c:if>
<c:if test="${empty param.password}">
<c:set var="passwordError" scope="request"
value="Password missing" />
<c:set var="isValid" value="false" />
</c:if>
<c:if test="${empty param.firstName}">
<c:set var="firstNameError" scope="request"
value="First Name missing" />
<c:set var="isValid" value="false" />
</c:if>
<c:if test="${empty param.lastName}">
<c:set var="lastNameError" scope="request"
value="Last Name missing" />
<c:set var="isValid" value="false" />
</c:if>
<c:if test="${empty param.dept}">
<c:set var="deptError" scope="request"
value="Department missing" />
<c:set var="isValid" value="false" />
</c:if>
<%-- Validate date by catching a possible exception --%>
<c:catch var="invalidDate">
<fmt:parseDate value="${param.empDate}" pattern="yyyy-MM-dd"
var="ignore" />
</c:catch>
<c:choose>
<c:when test="${empty param.empDate}">
<c:set var="empDateError" scope="request"
value="Employment Date missing" />
<c:set var="isValid" value="false" />
</c:when>
<c:when test="${invalidDate != null}">
<c:set var="empDateError" scope="request"
value="Invalid Employment Date" />
<c:set var="isValid" value="false" />
</c:when>
</c:choose>
<%-- Validate email address format using custom action --%>
<ora:ifValidEmailAddr value="${param.emailAddr}"
var="isValidEmailAddr" />
<c:choose>
<c:when test="${empty param.emailAddr}">
<c:set var="emailAddrError" scope="request"
value="Email Address missing" />
<c:set var="isValid" value="false" />
</c:when>
<c:when test="${!isValidEmailAddr}">
<c:set var="emailAddrError" scope="request"
value="Invalid Email Address" />
<c:set var="isValid" value="false" />
</c:when>
</c:choose>
<c:choose>
<c:when test="${isValid}">
<jsp:forward page="store.jsp" />
</c:when>
<c:otherwise>
<jsp:forward page="enter.jsp" />
</c:otherwise>
</c:choose>
At the top of Example
11-8, a <c:set> action creates a variable
named isValid with the value true. The rest
of the page validates each parameter value and sets this
variable to false if any value is found to be
invalid. This makes it easy to decide which page to forward to
at the end of the page. In addition, if any value is invalid,
another parameter-specific variable is created in the request
scope to hold the error message. As you will see later, these
error messages are added to the input page to tell the user
what's wrong.
For most parameters, a simple
<c:if> action that tests that some value is
submitted is all that's needed. But for the empDate
and emailAddr parameters, any old value isn't enough.
Verifying that a parameter value represents a real date is
tricky, since there are so many different ways to write a
date. In addition, you need to keep track of leap years, and
as you will see in Chapter
13, possibly deal with dates written in different
languages as well. Luckily, there's a JSTL action that knows
all these rules: the <fmt:parseDate> used in Example
11-1. If it's passed a date string that doesn't check out,
it throws an exception. Combined with the
<c:catch> action introduced in Chapter
9, this is all we need to validate a date. The
<fmt:parseDate> action is placed within a
<c:catch> action element, catching and saving a
possible exception in a variable named invalidDate. A
<c:choose> date then uses one
<c:when> action to test if a date string is
supplied at all, and if it is, tests if the
<fmt:parseDate> action threw an exception with
a second <c:when> action. I could have used
just a <c:if> action to test if an exception
was thrown, but the approach used here lets me provide
different error messages for no value and an invalid value.
The email address is validated with a custom
action named <ora:ifValidEmailAddr>, described in Table
11-8.
Table 11-8. Attributes for
<ora:ifValidEmailAddr>
value |
String |
Yes |
Mandatory. The value to
validate. |
var |
String |
No |
Optional. The name of the variable to
hold the result. |
scope |
String |
No |
Optional. The scope for the variable,
one of page, request,
session, or application. page
is the default. |
The action can be used with a body that's
evaluated only if the value has a valid email-address format
(contains only one at sign and at least one dot, e.g.,
"hans@gefionsoftware.com"), just like the
<c:if> action. Here the result is saved in a
variable instead and used in a <c:choose> block
to test for both no value and invalid value, the same as is
done for the date value.
If the request is forwarded back to the
enter.jsp page due to invalid input, the values the
user entered are used as the default values for the form
fields and the error messages are displayed next to each
field. Example
11-9 shows a part of the page for the User Name field.
Example 11-9. Displaying
error messages (enter.jsp) ...
<tr>
<td>User Name:</td>
<td><input type="text" name="userName"
value="<c:out value="${param.userName}" />">
</td>
<td><c:out value="${userNameError}" /></td>
</tr>
...
The first <c:out> action sets
the value of the input field to the corresponding parameter
value. The second <c:out> action uses the
userNameError variable created by the
validate.jsp page if the userName parameter
value is invalid and adds the message to the page. The results
are shown in Figure
11-8.
This is very similar to the examples in Chapter
8. The difference is that a separate page does the
validation, creating all error messages as request scope
variables that are then used in the input page if they exist,
instead of conditionally adding error messages defined in the
input page. Which approach is best is a matter of preference.
|