Wednesday, October 14, 2009

8.2 Validating User Input



[ Team LiB ]






8.2 Validating User Input



You
should never trust your users, at least not when it comes to entering
information in the format you need. Often, you need to make sure the
input is valid before you continue to process a request. A date, for
instance, can be written in many different formats. If
you've traveled to the United States, and
you're not a U.S. citizen, you probably have had to
fill out both an I-94 and a customs declaration form to be admitted
by an immigration officer. You may have noticed that on one of the
forms you need to write your birth date as
yy/mm/dd and on the other as
mm/dd/yy. I always get it wrong.



The entry form used in the examples in this chapter has a number of
fields that must be validated: a name must be entered, the birth date
must be a valid date, the email address must at least look like a
real email address (it's basically impossible to
verify that it is in fact real), the gender must be one of
m (male) or f (female), the
lucky number must be a number between 1 and 100, and if any food
favorites are selected, each must be one of z
(pizza), p (pasta), or c
(Chinese).



Simple input can be validated using the standard JSTL actions, but
for more complex validation rules, a bean is a good choice. We will
look at both approaches next. If you use JSP combined with servlets,
the input validation is typically done by the servlet and the JSP
pages are invoked only if the input turns out to be okay. This
approach is described in Chapter 19.




8.2.1 Validating User Input Using JSTL Actions



Besides adding
validation, let's make the input form example a bit
more realistic. Instead of just echoing the entered values at the end
of the page, we use them to set the initial values of the form
fields. This makes it easier for the user to correct the mistakes.
For each invalid value, an error message is also inserted above the
incorrect field.



I use a number of JSTL actions that we have not discussed so far and
a few tricks to implement these changes. To make all the new stuff
easier to digest, we look at the new page in pieces. Example 8-5 shows the top part of the form with the
validation and initialization of the Name field.




Example 8-5. Validating the name parameter with JSTL (validate_jstl.jsp)

<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
<title>User Info Entry Form</title>
</head>
<body bgcolor="white">

<form action="validate_jstl.jsp" method="post">
<input type="hidden" name="submitted" value="true">
<table>
<c:if test="${param.submitted && empty param.userName}">
<tr><td></td>
<td colspan="2"><font color="red">
Please enter your Name
</font></td></tr>
</c:if>
<tr>
<td>Name:</td>
<td>
<input type="text" name="userName"
value="<c:out value="${param.userName}" />">
</td>
</tr>



The first thing to notice in this example is the HTML field of type
"hidden", named submitted with
the value true. The browser
doesn't display a hidden field, but its value is
sent as a regular request parameter when the form is submitted. I use
it in this example to avoid displaying error messages when the page
is loaded for the first time, before the user has had a chance to
enter any data. The submitted parameter
isn't part of the first request for the page, but
when the user submits the form, the submitted
parameter is sent along with all parameters representing the other
HTML fields. Hence, it can be used to test if the parameters should
be validated or not.



The validation of the Name field illustrates how it works. The JSTL
<c:if> action, described in Table 8-5, is used with an EL expression that evaluates
to true only if the submitted
parameter has the value true and the
userName parameter is empty. Since the
submitted parameter isn't part of
the initial request to load the page, it doesn't
have the value true, causing the EL expression to
evaluate to false. The
<c:if> action's body is
therefore ignored in this case. After submitting the form, however,
the submitted parameter has the value
true, so if the userName
parameter contains an empty string (the user didn't
enter a value in the Name field), the body is processed, adding the
error message.



Table 8-5. Attributes for JSTL <c:if>

Attribute name



Java type



Dynamic value accepted



Description



test


boolean



Yes



Mandatory. An expression that evaluates to true or
false.



var


String



No



Optional. The name of the variable to hold the Boolean result.



scope


String



No



Optional. The scope for the variable, one of page,
request, session, or
application. page is the
default.




To make it easy for the user to correct mistakes, the form fields are
initialized with the submitted values. The
<c:out> action with an EL expression that
gets the corresponding parameter value takes care of this.



A note about the empty operator seems warranted,
because this is an operator you don't find in most
languages. It's included in the EL to avoid having
to deal with the difference between a null value
(the absence of a value) and the empty string value
("") because in a web application, you typically
want to treat both cases the same way. Without the
empty operator, you would have to write all tests
like the ones in Example 8-5 like this instead:



<c:if test="${param.submitted && 
(param.userName == null || param.userName == '')}">


The empty operator is shorthand for the
combination of the last two tests. In addition to empty strings and
null, it also evaluates to true
for an empty array, java.util.Collection, or
java.util.Map. In other words, you can use it to
test for empty collections of all types.



Another fairly unique feature in the EL is that you have a choice
with regards to the symbols for the common operators. For instance,
instead of using && as the logical AND
operator, || for logical OR, and
! for logical NOT, you can use
and, or, and
not. The relational operators can be written as
==, !=,
<, <=,
>, and >=, or as
eq, ne, lt,
le, gt, and
ge, respectively. Besides catering to different
personal preferences, the motivation for this is to provide a
consistent set of operator symbols for use in pure XML documents (as
described in Chapter 17) in which some of the most
commonly used symbols can cause problems (e.g.,
< and &&).



Example 8-6 shows the validation and initialization
of the Birth Date and Email Address fields.




Example 8-6. Validating the birth date and email parameters with JSTL (validate_jstl.jsp)

        <c:if test="${param.submitted && empty param.birthDate}">
<tr><td></td>
<td colspan="2"><font color="red">
Please enter your Birth Date
</font></td></tr>
</c:if>
<tr>
<td>Birth Date:</td>
<td>
<input type="text" name="birthDate"
value="<c:out value="${param.birthDate}" />">
</td>
<td>(Use format yyyy-mm-dd)</td>
</tr>
<c:if test="${param.submitted && empty param.emailAddr}">
<tr><td></td>
<td colspan="2"><font color="red">
Please enter your Email Address
</font></td></tr>
</c:if>
<tr>
<td>Email Address:</td>
<td>
<input type="text" name="emailAddr"
value="<c:out value="${param.emailAddr}" />">
</td>
<td>(Use format name@company.com)</td>
</tr>



As you can see, the processing for these fields is identical to the
pattern used for the Name field. A <c:if>
action tests if the form is submitted and the parameter corresponding
to the field is empty, and if so, adds an error message. The
submitted value of the field is added with a
<c:out> action.



For the Gender field (radio button), the value must be either
m (male) or f (female). This
requires a slightly different test condition, as shown in Example 8-7.




Example 8-7. Validating the gender parameter with JSTL (validate_jstl.jsp)

        <c:if test="${param.submitted &&
param.gender != 'm' && param.gender != 'f'}">

<tr><td></td>
<td colspan="2"><font color="red">
Please select a valid Gender
</font></td></tr>
</c:if>
<tr>
<td>Gender:</td>
<td>
<c:choose>
<c:when test="${param.gender == 'f'}">

<input type="radio" name="gender" value="m">
Male<br>
<input type="radio" name="gender" value="f" checked>
Female
</c:when>
<c:otherwise>

<input type="radio" name="gender" value="m" checked>
Male<br>
<input type="radio" name="gender" value="f">
Female
</c:otherwise>
</c:choose>
</td>
</tr>



In addition to testing if the form is submitted, we must test if the
value is m or f.
It's done by simply adding more subexpressions,
combined using the && operator. You can
combine as many subexpressions as you need in this way.



The Gender field isn't represented by a text field
but by a radio button, so another approach is also needed for
initializing it with the submitted value. To make a radio button be
displayed as selected, the checked attribute must
be added to the HTML element. The JSTL
<c:choose> action helps us with this task.



The <c:choose> action has no attributes; it
just groups and controls any number of nested
<c:when> actions and optionally one
<c:otherwise> action. These are the only
actions that are accepted as direct child elements of a
<c:choose> element. A
<c:choose> block is used to pick one of a
set of related, mutually exclusive alternatives. The
<c:choose> action makes sure that only the
first <c:when> action (Table 8-6) with a test attribute
value that evaluates to true is processed. If no
<c:when> action meets its test condition,
the <c:otherwise> body is processed instead.
If you're a programmer, you may recognize this as
being similar to a switch statement.



Table 8-6. Attributes for JSTL <c:when>

Attribute name



Java type



Dynamic value accepted



Description



test


boolean



Yes



Mandatory. An expression that evaluates to true or
false.




In Example 8-7, the
<c:choose> action contains one
<c:when> action that tests if the gender
parameter has the value f, and if so, adds both
radio button fields with the one representing the
f choice as selected. The
<c:otherwise> action adds the radio button
fields with the one representing m as selected.



The effect is that the m choice becomes the
default, used if the submitted value is invalid. It may seem
redundant to handle invalid values for a parameter representing a
radio button, but it isn't. Even though using a
group of radio buttons helps the regular user pick a valid value, you
must guard against requests submitted through other means than the
form. It's easy for someone to submit an HTTP
request to your page with any value. For instance, see what happens
if you request the page with a query string like this:



http://localhost:8080/ora/ch8/validate_jstl.jsp?submitted=true&gender=x


Since the page checks for valid values even for the radio buttons,
the x value for the gender
parameter results in an error message.



Next up is the processing of the Lucky Number field, in which the
value must be a number between 1 and 100. Example 8-8 shows how you can test for this.




Example 8-8. Validating the lucky number parameter with JSTL (validate_jstl.jsp)

        <c:if test="${param.submitted &&
(param.luckyNumber < 1 || param.luckyNumber > 100)}">

<tr><td></td>
<td colspan="2"><font color="red">
Please enter a Lucky Number between 1 and 100
</font></td></tr>
</c:if>
<tr>
<td>Lucky number:</td>
<td>
<input type="text" name="luckyNumber"
value="<c:out value="${param.luckyNumber}" />">
</td>
<td>(A number between 1 and 100)</td>
</tr>



Compared to the test for the Gender field, there's
one difference: the subexpressions for less than 1 or greater than
100 are placed within parentheses. Parentheses can be used in an EL
expression to override the default rules for in which order
subexpressions are evaluated, known as the
operator precedence
rules. The EL operator precedence rules say that
the && operator is evaluated before the
|| operator. Without the parentheses around the
range check, the expression is evaluated as "if
submitted, and the number is less than 1," and only
if that is false, evaluate "if
the number is greater than 100." With the
parentheses, it's evaluated as "if
submitted" and if that's
true, evaluate "if the number is
less than 1 or greater than 100." In this particular
case, the result would be the same, but when you mix
&& and || operators,
it's always a good idea to group the subexpressions
with parentheses to avoid surprises.



Example 8-9 shows the most complex validation case:
the list of food choices. Here the food parameter
may have none or many values, and each value must be one of
z (pizza), p (pasta), or
c (Chinese).




Example 8-9. Validating the food parameter with JSTL (validate_jstl.jsp)

        <c:forEach items="${paramValues.food}" var="current">
<c:choose>
<c:when test="${current == 'z'}">
<c:set var="pizzaSelected" value="true" />
</c:when>
<c:when test="${current == 'p'}">
<c:set var="pastaSelected" value="true" />
</c:when>
<c:when test="${current == 'c'}">
<c:set var="chineseSelected" value="true" />
</c:when>
<c:otherwise>
<c:set var="invalidSelection" value="true" />
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${invalidSelection}">

<tr><td></td>
<td colspan="2"><font color="red">
Please select only valid Favorite Foods
</font></td></tr>
</c:if>
<tr>
<td>Favorite Foods:</td>
<td>
<input type="checkbox" name="food" value="z"
${pizzaSelected ? 'checked' : ''}>Pizza<br>
<input type="checkbox" name="food" value="p"
${pastaSelected ? 'checked' : ''}>Pasta<br>
<input type="checkbox" name="food" value="c"
${chineseSelected ? 'checked' : ''}>Chinese
</td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Send Data">
</td>
</tr>
</table>
</form>
</body>
</html>



The approach I use for this test is to loop through all submitted
values (using the paramValues variable) with
<c:forEach>, testing each value with the
<c:choose> action and nested
<c:when> and
<c:otherwise> actions, setting a
"selected" variable to
true for each valid value and an
invalidSelection variable to
true for an invalid value. To set the variables, I
use the JSTL <c:set> action, described in
Table 8-7.



Table 8-7. Attributes for JSTL <c:set>

Attribute name



Java type



Dynamic value accepted



Description



value


Any type



Yes



Mandatory, unless the body is used to provide the value. The value to
set.



var


String



No



Optional. The name of the variable to hold the value. If not
specified, the target and
property attributes must be used.



scope


String



No



Optional. The scope for the variable specified by
var, one of page,
request, session, or
application. page is the
default.



target


A JavaBeans object or a java.util.Map



Yes



Optional. A Map or a JavaBeans object with a
property specified by property.



property


String



Yes



Optional. The property name for the object specified by
target that should be set.




Once these test variables are set based on the input,
it's easy to decide whether to add an error message;
just test if invalidSelection is
true.



The test variables also allow us to use the conditional EL operator
to decide when to add the checked attribute for
checkboxes:



<input type="checkbox" name="food" value="z"
${pizzaSelected ? 'checked' : ''}>Pizza<br>
<input type="checkbox" name="food" value="p"
${pastaSelected ? 'checked' : ''}>Pasta<br>
<input type="checkbox" name="food" value="c"
${chineseSelected ? 'checked' : ''}>Chinese


The conditional operator works with a Boolean subexpression (an
expression that evaluates to true or
false), followed by a question mark and two
alternative clauses separated by a colon. The first clause is used if
the Boolean expression evaluates to true; the
second clause is used if it's
false. If the test variable for the checkbox is
set to true, the text
"checked" is added; otherwise an
empty string is added.





8.2.2 Validating User Input Using a Bean



If you think
using JSTL to validate input looks complicated,
you're right. It works fine for simple validation,
like making sure a value has a value at all (as for the Name field)
or that a parameter has one of a few specific values (as for the
Gender choice). But with more complex validation, such as verifying
that a parameter holds an email address or a credit-card number, or
that a value matches a list of valid values held in a database, we
can do a lot better with a bean. In fact, the format of the Birth
Date and Email Address fields, and if the Lucky Number is something
else than a number, isn't checked at all in the JSTL
validation example. Other examples in this book will show how you can
use custom actions to do more thorough validation of these types of
values, but here we look at how it's done using a
bean. Figure 8-3 shows a typical response when some
fields have invalid values.




Figure 8-3. Response generated for invalid input



Since a bean is implemented with Java code and has access to all Java
APIs, it can do any kind of validation you can dream of. The
UserInfoBean used in the previous bean example
also has a number of validation properties, described in Table 8-8. If you're curious about the
bean implementation, it's described in Chapter 20.



Table 8-8. Validation properties for com.ora.jsp.beans.userinfo.UserInfoBean

Property name



Java type



Access



Description



userNameValid


boolean



Read



Is a user name set?



birthDateValid


boolean



Read



Is the birth date in the format yyyy-mm-dd?



emailAddrValid


boolean



Read



Is the email address in the format
name@company.com?



genderValid


boolean



Read



Is the gender m or f?



luckyNumberValid


boolean



Read



Is lucky number between 1 and 100?



foodValid


boolean



Read



Does the food list only contain z,
p, and c elements?



valid


boolean



Read



Do all properties have valid values?



pizzaSelected


boolean



Read



Is one of the elements in the food list a z?



pastaSelected


boolean



Read



Is one of the elements in the food list a p?



chineseSelected


boolean



Read



Is one of the elements in the food list a c?




All these properties are read-only, because the bean calculates their
values based on the properties holding user data. The first six
properties correspond one-to-one to the individual user data
properties, while the valid property provides an
easy way to see if all properties have valid values. The last three
aren't really validation properties; they tell if a
specific food type is part of the list of favorite foods.



These properties make the validation task much easier than in the
JSTL example. As before, we look at one piece at the time, starting
with the Name field processing in Example 8-10.




Example 8-10. Validating the name with a bean (validate_bean.jsp)

<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
<title>User Info Entry Form</title>
</head>
<body bgcolor="white">
<jsp:useBean id="userInfo"
class="com.ora.jsp.beans.userinfo.UserInfoBean">
<jsp:setProperty name="userInfo" property="*" />
</jsp:useBean>

<form action="validate_bean.jsp" method="post">
<input type="hidden" name="submitted" value="true">
<table>
<c:if
test="${param.submitted && userInfo.userNameValid == false}">

<tr><td></td>
<td colspan="2"><font color="red">
Please enter your Name
</font></td></tr>
</c:if>
<tr>
<td>Name:</td>
<td>
<input type="text" name="userName"
value="<c:out value="${userInfo.userName}" />">
</td>
</tr>



Like in Example 8-4, the
<jsp:useBean> and
<jsp:setProperty> actions capture the user
input. The only difference is that these action elements are now at
the top of the page. The bean is created and initialized before it
tests for valid input and fills out the form with the previously
entered values. Using the hidden field to avoid displaying error
messages the first time the page is loaded is a trick we used in the
JSTL version of the page as well.



The validation and setting the field value is a little bit different
than in the JSTL example, but not much. Instead of testing if the
userName parameter is equal to an empty string,
the userNameValid bean property is compared to the
Boolean value false. Even though it
doesn't look like we have simplified life much, we
have. All logic for deciding what is a valid value is now
encapsulated in the bean instead of being coded in the page. If at a
future date you decide to develop stricter rules for what a name must
look like (maybe scan for profanities), you have to change only the
bean; all pages where the bean is used remain the same. The Name
field is then set to the value the user submitted, if any, with a
<c:out> action using the
bean's userName property value.



Example 8-11 shows how the birth date value is
processed.




Example 8-11. Validating the birth date with a bean (validate_bean.jsp)

        <c:if test="${param.submitted && !userInfo.birthDateValid}">
<tr><td></td>
<td colspan="2"><font color="red">
Please enter a valid Birth Date
</font></td></tr>
</c:if>
<tr>
<td>Birth Date:</td>
<td>
<input type="text" name="birthDate"
value="<c:out value="${userInfo.birthDate}" />">
</td>
<td>(Use format yyyy-mm-dd)</td>
</tr>



Testing the value of the bean's
birthDateValid property, following the same
pattern as for the name, handles the validation. But if you look
carefully, you notice that instead of testing for equality with the
value false, this test uses the
!userInfo.birthDateValid syntax instead. This is
just shorthand for the same kind of test. The !
operator means "if the value is
true, treat it as false, and
vice versa." Formally, this operator is called the
logical complement
operator. I normally use the shorthand syntax
because it's easier to type.



What's more interesting in Example 8-6 than the syntax difference is that as with the
name parameter test, all validation logic is encapsulated in the
bean. Testing if a date is valid can be quite a challenge. For
instance, February 29 is a valid date only for a leap year. By
delegating the validation to the bean and using only the result in
the JSP page, the page author doesn't need to know
any of these details. The Birth Date field value is set by, you
guessed it, a <c:out> action using the
bean's birthDate property.



The Email Address and the Lucky Number fields are handled the same
way as Name and Birth Date.



The Gender field is dealt with pretty much the same as in the JSTL
version, as shown in Example 8-12.




Example 8-12. Validating the gender choice with a bean (validate_bean.jsp)

        <c:if test="${param.submitted && !userInfo.genderValid}">
<tr><td></td>
<td colspan="2"><font color="red">
Please select a valid Gender
</font></td></tr>
</c:if>
<tr>
<td>Gender:</td>
<td>
<c:choose>
<c:when test="${userInfo.gender == 'f'}">

<input type="radio" name="gender" value="m">
Male<br>
<input type="radio" name="gender" value="f" checked>
Female
</c:when>
<c:otherwise>

<input type="radio" name="gender" value="m" checked>
Male<br>
<input type="radio" name="gender" value="f">
Female
</c:otherwise>
</c:choose>

</td>
</tr>



The only differences are that the bean's
genderValid property is used for the validation
test, and the gender property is used to decide
which choice to mark as checked, instead of the parameter value used
for both these tasks in the JSTL version.



Example 8-13 shows that the biggest bang for the buck
we get from using a bean instead of just JSTL is the simplified
processing of the favorite food choices.




Example 8-13. Validating the food choices with a bean (validate_bean.jsp)

        <c:if test="${param.submitted && !userInfo.foodValid}">
<tr><td></td>
<td colspan="2"><font color="red">
Please select only valid Favorite Foods
</font></td></tr>
</c:if>
<tr>
<td>Favorite Foods:</td>
<td>
<input type="checkbox" name="food" value="z"
${userInfo.pizzaSelected ? 'checked' : ''}>Pizza<br>
<input type="checkbox" name="food" value="p"
${userInfo.pastaSelected ? 'checked' : ''}>Pasta<br>
<input type="checkbox" name="food" value="c"
${userInfo.chineseSelected ? 'checked' : ''}>Chinese
</td>
</tr>



All the looping and testing of the individual values that is
necessary in the JSTL version of the page are now encapsulated in the
bean, so all that's needed here is to use the
bean's properties to decide whether to add an error
message and which checkboxes to check.








    [ Team LiB ]



    No comments:

    Post a Comment