Form Validation with Perl and CGI

The most reliable way to make sure your users haven't filled in your forms wrongly is to use server-side form validation. This tutorial shows how you can do this with Perl and the CGI, and introduces the concept of subroutines along the way.

In this tutorial we're going to write a CGI script that checks whether a form has been filled out correctly. Along the way, you'll also learn about the concepts of reentrant forms and Perl subroutines. Read on!

Why use form validation?

Form validation prevents visitors to your site from accidentally sending you forms with missing or incorrect data. This can save you (and them) a lot of time and trouble!

It's possible to do form validation using JavaScript (see our Form validation with JavaScript tutorial). JavaScript validation is nice and quick because there's no round-trip to the server; however Perl validation is more secure and reliable, because it will always work even if the browser has JavaScript disabled, or if the user has managed to "hack" the JavaScript validation.

Reentrant forms

If you've followed our Writing a simple form mailer tutorial, you'll know how to write a simple Perl form handler that uses a static HTML page to display the form. In this tutorial, we're going to go one step better, and create our HTML form using the Perl script itself!

By doing this, we can re-display the form, along with any data entered by the user and an error message, if the user fills in the form incorrectly. This makes the form handling nice and friendly for the visitor.

This technique is often referred to as creating a reentrant form.

The form display subroutine

In order to create our reentrant form, we're going to write a subroutine to display the form.

Subroutines, like functions in JavaScript, give you an easy way to run a common function more than once in your script. Once you place your code within a subroutine block, you can run that code as often as you like, from anywhere in the script. What's more, you can pass parameters into the subroutine, and make it do different things depending on those parameters.

In our example, we're going to create a subroutine that displays the HTML form. We'll be calling this subroutine in two ways: firstly, when the blank form is first displayed to the visitor; and secondly, when we need to redisplay the form (including the visitor's data and any error message) in the event of the user sending us an incorrect form.

To do this, we need to pass any form field values that the user has sent us, and the error message, into the subroutine.

Our form will contain the following fields:

  • "Your Name" (text box)
  • "Your Sex" (radio button)
  • "Your Age" (drop-down list)

Here is our form display subroutine in full:


sub display_form
{
    my $error_message = shift;
    my $your_name = shift;
    my $your_sex = shift;
    my $your_age = shift;

    # Remove any potentially malicious HTML tags
    $your_name =~ s/<([^>]|\n)*>//g;

    # Build "selected" HTML for the "Your Sex" radio buttons
    my $your_sex_f_sel = $your_sex eq "f" ? " checked" : "";
    my $your_sex_m_sel = $your_sex eq "m" ? " checked" : "";

    # Build "selected" HTML for the "Your Age" drop-down list
    my $your_age_html = "";
    my @your_age_opts = ("Please select","Under 18","18-35","35-55","Over 55");

    foreach my $your_age_option ( @your_age_opts )
    {
        $your_age_html .= "<option value=\"$your_age_option\"";
        $your_age_html .= " selected" if ( $your_age_option eq $your_age );
        $your_age_html .= ">$your_age_option</option>";
    }

    # Display the form
    print <<END_HTML;
    <html>
    <head><title>Form Validation</title></head>
    <body>

    <form action="form_validation.cgi" method="post">
    <input type="hidden" name="submit" value="Submit">

    <p>$error_message</p>

    <p>Your Name:<br>
    <input type="text" name="your_name" value="$your_name">
    </p>

    <p>Your Sex:<br>
    <input type="radio" name="your_sex" value="f"$your_sex_f_sel>Female
    <input type="radio" name="your_sex" value="m"$your_sex_m_sel>Male
    </p>

    <p>Your Age:<br>
    <select name="your_age">$your_age_html</select>
    </p>

    <input type="submit" name="submit" value="Submit">

    </form>

    </body></html>
END_HTML

}

The first four lines use Perl's shift function to retrieve the parameters passed into it (if any). We then dynamically create and display the HTML for the form, pre-filling the form fields with any values that the user has already entered (passed via the parameters). The subroutine will also display any error message (again, passed as a parameter) at the top of the form.

Note that we're using a regular expression to filter the $your_name variable and remove any HTML tags before we display it in our form. This is a good idea as it will limit the risk of form hacking and cross-site-scripting attacks.

When writing real-world Web applications and scripts, always filter any data that the script receives from an untrusted source. This includes any data from form posts, GET variables in query strings, uploaded files and anything else that might have been created by someone, or something, you don't control.

The form contains a submit button called submit - our script can later use this field to determine if the form has been submitted yet or not. For backup, the same field is defined as a hidden field, in case the user just presses the Enter key rather than clicking on the "Submit" button.

The form validation subroutine

We'll now write the subroutine that checks the form fields entered by the user. If all the fields have been filled in correctly, we will return a success value (in this case, 1). If any fields are missing, we will call our reentrant form display subroutine above, passing in any fields that the user has entered, along with the error message, as parameters to the subroutine. We then return a failure value of 0.


sub validate_form
{
    my $your_name = $query->param("your_name");
    my $your_sex = $query->param("your_sex");
    my $your_age = $query->param("your_age");

    my $error_message = "";

    $error_message .= "Please enter your name<br/>" if ( !$your_name );
    $error_message .= "Please specify your sex<br/>" if ( !$your_sex );
    $error_message .= "Please specify your age<br/>" if
    ( $your_age eq "Please select" );

    if ( $error_message )
    {
        # Errors with the form - redisplay it and return failure
        display_form ( $error_message, $your_name, $your_sex, $your_age );
        return 0;
    }
    else
    {
        # Form OK - return success
        return 1;
    }
}

We've used Perl's string concatenation operator (.=) to append any error messages to the $error_message string. We can then test this string at the end of our checks to determine if it's empty or not. If it isn't empty, then we know there were errors, and we can redisplay the pre-filled form along with the error message, and return the failure code.

We can test if a form field is empty (i.e. the user hasn't entered anything) by using the not (!) operator. This returns a true value if the tested variable is empty. For example:


    $error_message .= "Please enter your name</br>" if ( !$your_name );

Putting it all together

We'll now write the rest of the script, including the decision-making logic. Here it is:


#!/usr/bin/perl

use CGI;

# Create the CGI object
my $query = new CGI;

# Output the HTTP header
print $query->header ( );

# Process form if submitted; otherwise display it
if ( $query->param("submit") )
{
    process_form ( );
}
else
{
    display_form ( );
}

sub process_form
{
    if ( validate_form ( ) )
    {
        print <<END_HTML;
        <html><head><title>Thank You</title></head>
        <body>
        Thank you - your form was submitted correctly!
        </body></html>
END_HTML
    }
}

This code just initialises the CGI query object, displays the HTTP header, and then calls either the process_form() subroutine or the display_form() subroutine, depending on whether the form has been submitted or not. (We test the value of the form field submit to determine if the form has been submitted - if the field is non-empty then the test returns true.)

The process_form() subroutine calls validate_form(), then simply displays a thank-you message if the form was filled in correctly. In a "real-world" application you would do all your actual form processing here (sending an email, adding a record to a database table, etc).

The finished script

You can view the whole script here, and see the script in action here. Try submitting the form without filling in all the fields, and see the results!

Follow Elated

Related articles

Responses to this article

13 responses (oldest first):

16-Nov-09 12:19
The example script which has given here is truely wonderful and much helpful for me. Thanks a lot.
17-Nov-09 00:28
You're welcome, perlprofesional - I'm glad it helped!
Matt
12-Apr-10 01:40
Hi, I'm new to perl scripting, but the article was great, so I followed along and think I have most of my script under control, but seem to be getting errors related to the radio buttons in the 'display_form' function.

My browser returns:
"Premature end of script headers ..."

So in my Shell window when doing "perl -w" on my script I get:
Use of unitialized value in string eq at myscript.cgi line ...

I think this is what it's referring to:



# Build selected HTML for the support radio buttons
$support_rspca_sel = $support eq "RSPCA" ? " checked" : "";
$support_awl_sel = $support eq "AWL" ? " checked" : "";



Then I refer to that later in my form:



<label><input type="radio" name="support" value="AWL"$support_awl_sel>AWL (Qld)</label>
<br>
<label><input type="radio" name="support" value="RSPCA"$support_rspca_sel>RSPCA (Qld)</label>



My syntax check came back with an OK.
What am I missing? Thanks for your time on this. Much appreciated.
Carly
16-Apr-10 03:02
Hi Carly,

Actually the "uninitialized value" error is probably because you turned warnings on with -w. It may not be what's causing your 500 server error (unless you're also using #!/usr/bin/perl -w at the top of your script?).

The best bet is probably to use the CGI::Carp module to display errors in the browser, rather than the generic 500 error:

http://perldoc.perl.org/CGI/Carp.html

In other words, add this below the "use CGI;" line in your script:


use CGI::Carp qw(fatalsToBrowser);


You'll then see the exact error message in your browser when you run the script.

Does that help?
18-Apr-10 23:38
Thanks Matt. It's running now. I'm still having a few issues but I'm just removing the code and putting pieces back in to figure it out. Just taking it one thing at a time Will get there eventually!
Carly
20-Apr-10 21:31
No probs Carly. Feel free to ask for more help if you need it!
02-Aug-10 17:51
Hi, i need some help regarding making HTML forms with perl scripting for the server side. As I am using oracle database for my project, I know how to make the connectiong but i need to know a few things..

1. If am using Perl, is it better to embed it in HTML forms or Is it better to make HTML forms and then post them to Perl and use Perl for server scripting?

2. I need to insert a lot of data into the database, so, the HTML forms will be quite lengthy, so in your opinion which option is better and where can i find examples for the chosen method..

Please Help!!! your help will be really appreciated.
02-Aug-10 18:23
@karan: Your questions don't make a lot of sense. Are you talking about form validation, or form processing?

You can't normally embed Perl within HTML unless you're using a library such as HTML::Embperl.
02-Aug-10 20:13
I am not talking about form validation here. sorry the question got posted under the wrong topic. I need to simply create HTML forms which will insert the data into database.
for inserting it into database i am using perl (coz seemingly only tat works on the university's server for oracle connection)

my question is how can i use perl as an interface between html and oracle. are there any examples? I am relatively new to perl, hence jumbled questions.
02-Aug-10 20:25
@karan: The script at http://www.elated.com/articles/form-validation-with-perl-and-cgi/ should actually give you a good starting point. Instead of displaying the entered form data in the page, you'd instead insert the data into your Oracle database using SQL INSERT statements. You'll probably want to use DBI for this - here are some good articles on the topic:

http://www.dba-oracle.com/t_dbi_interface1.htm
18-Apr-11 08:46
hi... the script is great and was a good starting point for form validation... i am facing difficulties in reading <textarea> input... i am able to grab only the first line in the text area... can u please help...

thanks
dhina
18-Apr-11 09:15
hello sir...
sorry... i fixed the script.. mistake was mine... i just read the other related posts and fixed the error!

thanks
20-Apr-11 02:18
@dhina: No problem - I'm glad you got your script working.

Post a response

Want to add a comment, or ask a question about this article? Post a response.

To post responses you need to be a member. Not a member yet? Signing up is free, easy and only takes a minute. Sign up now.

Top of Page