Creating Reusable Server-Side Form Validation Routines

Earlier in this chapter, in Section 5.1.1, we discussed the three common steps taken to create a form:

  1. Create an HTML form with the proper form fields, complete with client-side form validation.

  2. Create a form-processing script and add the needed code.

  3. 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.

Warning

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:

  1. Determine what Request collection to use. (If this isn’t specified via the Collection HIDDEN variable, the Request.Form collection is used by default.)

  2. Iterate through the proper collection, checking to see what form fields contain an exclamation point.

  3. 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.

  4. If the Test method returns False, then the input is invalid. Note this invalid entry in an error log.

  5. 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 pattern
        objRegExp.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.

/CODEREUSE/ValidateForm.asp validates all forms on the site

Figure 5-1. /CODEREUSE/ValidateForm.asp validates all forms on the site

Now, let’s look at how to encapsulate the logic in /CODEREUSE/ValidateForm.asp through the use of classes!

Using Classes to Enhance Server-Side Validation

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.

Tip

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 objRegExp
     Private 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 log
        Set objErrorDict = CreateObject("Scripting.Dictionary")
     End Sub

     Private Sub Class_Terminate(  )
        Set colRequestCol = Nothing
        Set objRegExp = Nothing
        Set 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 log
             objErrorDict.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.asp
    Dim objFormData
    Set objFormData = New FormData

    'Test to see if form inputs are valid
    If objFormData.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 message
      Response.Write objFormData.ErrorMessage(  )
    End if

    Set objFormData = Nothing
%>

Tip

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.

Further enhancements to the server-side validation class

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.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.107.90