Earlier in this chapter, in Section 5.1.1, we discussed the three common steps taken to create a form:
Create an HTML form with the proper form fields, complete with client-side form validation.
Create a form-processing script and add the needed code.
Add server-side form validation code to the ASP page created in Step 2.
At that time, I mentioned we would look at ways to create robust, reusable code for Steps 1 and 3. Since server-side form validation is paramount to client-side form validation, it makes sense to start by creating code for Step 1. In this section, we will look at how to create a single, reusable ASP page that uses regular expressions to validate form data. In Section 5.5, the discussion will switch to creating a class interface to aid in accomplishing Step 1.
Our reusable server-side form validation routine will exist on the
/CODEREUSE/ValidateForm.asp
ASP
page. (That means every form on our web site
will have its ACTION
property set to
/CODEREUSE/ValidateForm.asp
.) This page will
contain code that will iterate through the proper collection of the
Request object, reading in the value of each form field and
determining whether or not the value is valid.
Our validation page,
/CODEREUSE/ValidateForm.asp
, needs to know what
constitutes a valid entry for each and every form field. We’ll
supply this information in the NAME
property of
the form field. Since regular expressions are used to validate form
field input, each form field that needs to be validated will contain
the proper regular expression appended to the NAME
property. For example, if we had a form field for the user to enter
his telephone number, we would use the following HTML to create the
text box:
<FORM METHOD="POST" ACTION="/CODEREUSE/ValidateForm.asp"> <INPUT TYPE="HIDDEN" NAME="Collection" VALUE="Form"> <INPUT TYPE="HIDDEN" NAME="Redirect" VALUE="/SomePage.asp"> <!-- This will be validated, since the element name contains an exclamation point followed by the needed regular expression. --> Enter your phone number in the following format: ###-###-####<BR> <INPUT TYPE="TEXT" NAME="Phone Number!^d{3}-d{3}-d{4}$"> <P> <!-- This form field will NOT be subjected to any validated tests since it does not contain the exclamation point in its name. --> Enter your first name:<BR> <INPUT TYPE="TEXT" NAME="First Name"> <P> <INPUT TYPE="SUBMIT" VALUE="Submit!"> </FORM>
Note that an exclamation point separates the English-like name of the
form field (txtPhone
) and the regular
expression (d{3}-d{3}-d{4}
). The regular
expression will be used to validate the user’s input in
/CODEREUSE/ValidateForm.asp
. Also note that the
form’s ACTION
property directs the submitted
form to /CODEREUSE/ValidateForm.asp
.
The above form also contains two HIDDEN
form
fields. The first HIDDEN
form field,
Collection
, specifies what Request object
collection to use. The Value
for this
HIDDEN
form field would have been
QueryString
, had the form’s
METHOD
been set to GET
. The
second HIDDEN
form field,
Redirect
, specifies the ASP page that is
responsible for processing the form data. This page (via a
Server.Transfer) is called only if the submitted form data is valid.
Be absolutely sure to place the quotes around the
form field NAME
value! If
not, any spaces in the form field NAME
will cut
off the rest of the NAME
when passed to the
server-side validation script. If you need to have double quotes in
the regular expression, you must represent the double quote with some
other character and have it transformed back into a double quote in
the server-side validation script.
The code for /CODEREUSE/ValidateForm.asp
will
perform the following steps:
Determine what Request collection to use. (If this isn’t
specified via the Collection
HIDDEN
variable, the Request.Form collection is
used by default.)
Iterate through the proper collection, checking to see what form fields contain an exclamation point.
For those elements that do contain an exclamation point, read in the
regular expression pattern and perform a regular expression match
using the Test
method of the
RegExp
object.
If the Test
method returns False, then the input
is invalid. Note this invalid entry in an error log.
Once the entire collection has been iterated, determine whether any
validation errors occurred. If so, output these errors. If not, use
Server.Transfer
to forward the user onto the
form-processing script specified by the HIDDEN
variable Redirect
.
Quite a tall order for
/CODEREUSE/ValidateForm.asp
! Here is the code,
with the regular expression validation emphasized:
<%@ LANGUAGE = "VBSCRIPT" %> <% Option Explicit %> <% Function EnglishName(str) 'Check to see if there is an exclamation point: if so, hack off 'all contents of the string to the right of the exclamation point If InStr(1,str,"!") > 0 then EnglishName = Left(str,InStr(1,str,"!") - 1) End If End Function 'Determine what collection to use Dim strCollection strCollection = Request("Collection") 'Create a reference to the property Request collection Dim colRequestCol If Ucase(strCollection) = "QUERYSTRING" then Set colRequestCol = Request.QueryString Else Set colRequestCol = Request.Form End If Dim strItem, strErrors, objRegExp, strErrorLog Set objRegExp = New regexp ' Create a regexp instance objRegExp.IgnoreCase = True objRegExp.Global = True 'Iterate through each of the form field elements For Each strItem in colRequestCol 'See if there is an exclamation point. If there is, 'then we need to perform form validation If InStr(1,strItem,"!") > 0 then 'Grab the regular expression patternobjRegExp.Pattern = Mid(strItem, InStr(1,strItem,"!") + 1, Len(strItem))
If Not objRegExp.Test(colRequestCol(strItem)) then
'Input invalid! Append to the error log
strErrorLog = strErrorLog & "<BR>Invalid Input for " & _
EnglishName(strItem)
End If
End If Next 'Are there any errors? If Len(strErrorLog) > 0 then Response.Write "<B>The following validation errors occurred:</B>" Response.Write strErrorLog Else 'No form validation errors occurred! 'Use Server.Transfer to send the user to the proper 'form validation script 'If the user didn't specify a redirect, raise an error If Len(Request("Redirect")) = 0 then Raise vbObjectError + 1010, "Validation Error", "Redirect not specified" Else Server.Transfer Request("Redirect") End If End If Set colRequestCol = Nothing ' Clean up Set objRegExp = Nothing %>
Keep in mind that /CODEREUSE/ValidateForm.asp
serves only one purpose—to perform server-side validation. If
there are no validation errors, the script transfers control to the
form-processing page specified by the HIDDEN
form
field Redirect
. Due to the fact that
/CODEREUSE/ValidateForm.asp
only performs
server-side validation and transfers the user to the correct
form-processing script, all of our HTML pages can call this single
validation page. This means once this system is implemented, when
creating a form, you will not have to code its server-side
validation! Figure 5.1 further illustrates the page
flow that occurs when a form is submitted.
Now, let’s look at how to encapsulate the logic in
/CODEREUSE/ValidateForm.asp
through the use of
classes!
As discussed in Chapter 4, classes provide
a means to encapsulate complexity. Our first implementation of
/CODEREUSE/ValidateForm.asp
is rich in
complexity. The developer who wishes to make any modifications to
this page must know that the names of form variables may contain an
exclamation point and regular expression code. It would be nice to
hide such complexity.
There is also a lot of complexity in creating the HTML forms. We’ll look at ways to reduce this complexity using classes later on!
When encapsulating the server-side form-validation routines into a
class, the functionality remains constant—only the
implementation changes. For that reason, creating the class is mostly
cut and paste work! A few enhancements have been made to the new
class version, though. The major enhancement is the use of a
Dictionary object to store the validation error information
(objErrorDict
). The changes made due to
this enhancement are emphasized in the following code snippet:
<% Class FormData '****************** PROPERTIES ******************** Private colRequestCol Private objRegExpPrivate objErrorDict
'************************************************** '************** INITIALIZE/TERMINATE ************** Private Sub Class_Initialize( ) 'Determine what Request collection to use If Ucase(Request("Collection")) = "QUERYSTRING" then Set colRequestCol = Request.QueryString Else Set colRequestCol = Request.Form End If 'Instantiate a regexp object Set objRegExp = New regexp objRegExp.IgnoreCase = True objRegExp.Global = True 'Instantiate the error logSet objErrorDict = CreateObject("Scripting.Dictionary")
End Sub Private Sub Class_Terminate( ) Set colRequestCol = Nothing Set objRegExp = NothingSet objErrorDict = Nothing
End Sub '************************************************** '************* PROPERTY GET STATMENT **************Public Property Get ErrorLog( )
'Return the Error Log Dictionary Object
Set ErrorLog = objErrorDict
End Property
'************************************************** '********************* METHODS ******************** Public Function ValidInputs( ) 'Checks to see if data is valid. If it is, returns 'True, else returns False. A list of errors can be 'obtained through PrintErrors Dim strItem 'Iterate through each of the form field elements For Each strItem in colRequestCol 'See if there is an exclamation point. If there is, 'then we need to perform form validation If InStr(1,strItem,"!") > 0 then 'Grab the regular expression pattern objRegExp.Pattern = Mid(strItem, InStr(1,strItem,"!") + 1, _ Len(strItem)) If Not objRegExp.Test(colRequestCol(strItem)) then 'Input invalid! Append to the error logobjErrorDict.Add EnglishName(strItem), _
colRequestCol(strItem).Item
End If End If Next 'Did we encounter any errors? If objErrorDict.Count > 0 then ValidInputs = False Else ValidInputs = True End If End Function Private Function EnglishName(ByVal str) 'Check to see if there is an exclamation point: if so, hack off 'all contents of the string to the right of the exclamation point If InStr(1,str,"!") > 0 then str = Left(str,InStr(1,str,"!") - 1) End If EnglishName = Replace(str, "_", " ") End Function Public Function ErrorMessage( ) 'Returns a string containing an error message If objErrorDict.Count = 0 then ErrorMessage = "There were no validation errors." Else Dim strName For Each strName in objErrorDict ErrorMessage = ErrorMessage & "Error in " & strName & _ " - the entry " & objErrorDict(strName) & _ " is invalid." & vbCrLf Next End If End Function '************************************************** End Class %>
If we place the above class definition into a file, then we can use a
server-side include to import the class definition into the ASP pages
that are interested in using the class. Classes reduce complexity by
hiding implementation details, and as they say, the proof is in the
pudding. Previously, /CODEREUSE/ValidateForm.asp
consisted of 57 lines of code. Using classes and include files, the
line count has been reduced drastically to 18 lines!
<%@ LANGUAGE = "VBSCRIPT" %> <% Option Explicit %> <!--#include virtual="/CODEREUSE/FormData.Class.asp"--> <% 'Instantiate our class defined in FormData.Class.aspDim objFormData
Set objFormData = New FormData
'Test to see if form inputs are valid IfobjFormData.ValidInputs( )
then 'No form validation errors occurred! 'Use Server.Transfer to send the user to the proper 'form validation script 'If the user didn't specify a redirect, raise an error If Len(Request("Redirect")) = 0 then Raise vbObjectError + 1010, "Validation Error", "Redirect not specified" Else Server.Transfer Request("Redirect") End If Else 'Display an error messageResponse.Write objFormData.ErrorMessage( )
End if Set objFormData = Nothing %>
If you are a little rusty on using include files within an ASP page, be sure to review Chapter 1 and check out the following online article “The low-down on #includes” at http://www.4GuysFromRolla.com/webtech/080199-1.shtml.
There are, of course, numerous improvements that could be made to the server-side validation class. One major improvement would be to have a method that not only displays the invalid form entries, but also displays the form the user just filled out, so they can quickly make the changes they need to.
One possible way to accomplish this would be to have the form supply
yet another HIDDEN
variable that would contain the
URL to the HTML form. Then a method could be added that would perform
a Server.Transfer back to the form after displaying the invalid form
entries. That way, the user would see a list of the invalid form
fields at the top of the HTML page, and then would be shown the
form
again. (This would save the user from
having to click the back button when he or she has entered invalid
form
data.)
18.117.107.90