Using Ajax from Perl
by Dominic Mitchell
|
Pages: 1, 2, 3, 4
Ajax and Perl: CGI::Ajax
You could spend a lot of time figuring out all the pieces of JavaScript on the client side and Perl on the server side in order to work out how to use Ajax in your code. However, this is Perl; we like to be a bit lazy. Thankfully, there's already a module on CPAN to take the pain out of it: CGI::Ajax.
CGI::Ajax provides a small bit of infrastructure for your CGI programs. You tell it about some of your functions and it sets up JavaScript to call them and return the results to your page. You don't need to worry about writing the JavaScript to do this, because CGI::Ajax takes care of it. All you have to do is add some JavaScript calls to the functions defined in your script and let CGI::Ajax deal with the plumbing.
The functions that CGI::Ajax creates in JavaScript for you all follow more or less the same pattern. They take two parameters: a list of HTML IDs to get input from, and a second list of HTML IDs to insert the results into. Having ID attributes in your HTML is a prerequisite for enabling this behavior. CGI::Ajax handles querying your web page for the input values and inserting the results when the answer comes back from the server.
By making functions in your CGI script available to the browser, you have the ability to do things that you can't ordinarily do. For instance, you can look up values in a database, or query the system load average. Anything you can do in Perl that you couldn't in JavaScript now becomes possible.
Checking Usernames
To explore CGI::Ajax, consider a typical problem. You have a signup page for an application. You have to enter a username and password in order to register with the application. Because this is such a wildly popular application, the username you want is probably taken. Unfortunately, you have to resubmit the entire form and wait for the server to receive it before being told that you can't have the username fluffykitten. How can CGI::Ajax can help you to solve this?
Start by making a basic CGI script to handle registrations. I'm going to make it very minimal in order to try and focus on the problem at hand. Feel free to embellish it once you have it up and running. I'll walk through the script below, but you can also download the registration script.
It starts off like all good Perl code, by using the strict and warnings modules, followed by the only other necessary module: CGI.pm. It creates a new CGI object and then calls main() to do the real work.
#!/usr/local/bin/perl
# User registration script.
use strict;
use warnings;
use CGI;
my $cgi = CGI->new();
main();
Most of main() deals with putting together bits of HTML. For a real-world script, use something like HTML::Template or Template Toolkit instead of putting HTML directly in the script.
The interesting stuff happens in the middle. First it checks for a passed-in user parameter. If so, the code checks that it looks good and records that username in our database. If there were any problems, it mentions those as well. At the end, it sends the created HTML to the browser for display.
sub main {
my $html = <<HTML;
<html><head>
<title>Ordinary CGI demo</title>
</head><body>
<h1>Signup!</h1>
HTML
if ( my $user = $cgi->param('user') ) {
my $err = check_username( $user );
if ( $err ) {
$html .= "<p class='problem'>$err</p>";
} else {
save_username( $user );
$html .= "<p>Account <em>$user</em> created!</p>\n";
}
}
my $url = $cgi->url(-relative => 1);
$html .= <<HTML;
<form action="$url" method="post">
<p>Please fill in the details to create a new Account.</p>
<p>Username: <input type="text" name="user" id="user"/></p>
<p>Password: <input type="password" name="pass" id="pass"/></p>
<p><input type="submit" name="submit" value="SIGNUP"/></p>
</form></body></html>
HTML
print $cgi->header();
print $html;
}
In order to implement a user database simply, I decided to store a list of users in a text file, one per line. To check if a username is taken, the code reads the file and compares each line to the passed-in value, case-insensitively. If there are any problems, they're returned as a string. If the file doesn't exist, then the code allows any username (this helps to avoid errors the first time you run the script). Again, a real application would probably store users in a database using DBI.
sub check_username {
my ( $user ) = @_;
return unless -f '/tmp/users.txt';
open my $fh, '<', '/tmp/users.txt'
or return "open(/tmp/users.txt): $!";
while (<$fh>) {
chomp;
return "Username taken!" if lc $_ eq lc $user;
}
return;
}
Lastly, in order to save a username, the code must append it onto the end of the file.
sub save_username {
my ( $user ) = @_;
open my $fh, '>>', '/tmp/users.txt'
or die "open(>>/tmp/users.txt): $!";
print $fh "$user\n";
close $fh;
return;
}
Now you should have a script that lets you enter usernames and record them into a file. If you try to enter the same name twice, it will stop you, just like Hotmail. Now imagine that, like Hotmail, you also had to spend a while trying to interpret a captcha image as well. Then, when you've finally managed to work out what letter that strange squiggle represents, you hit Submit only to be told that the username you entered doesn't matter. Then you have to think of another username and try to decipher another set of odd-looking lines. You can see it's important to tell the user as soon as possible that a username is unavailable.

