Book: JavaServer Pages™, 2nd Edition
Section: Chapter 11.  Accessing a Database



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>

Attribute name

Java type

Dynamic value accepted

Description

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.

Figure 11-8. The input page with error messages
figs/Jsp2_1108.gif

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.

    [http://safari.oreilly.com/059600317X/jserverpages2-CHP-11-SECT-2]


    Copyright © 2002 O'Reilly & Associates, Inc. All rights reserved.
    1005 Gravenstein Highway North
    Sebastopol, CA 95472