What follows is a short introduction to CGI applications development using Perl and its 'CGI::Application' module. You can find much more information by reading the package documentation. Think of this article as a starting point to gain an insight into it and decide if it's worth delving.
I started writing Perl CGI applications some years ago, at that time I used to fill my code with
print "<html><head><title>This is the title</title></head><body>" print "<p>this is a paragraph</p>" print "</body></html>"
I realized very early that there was a better solution, the 'CGI' package.
After some more time, looking for a way to implement the "divide et impera" principle, I started using another good package: 'HTML::Template'. With this I reached a fairly good point, as I could definitely split HTML and Perl code. At that time I was quite satisfied as I could first concentrate on my application's engine and then choosing the best coachwork for them. Well... I'm not talented for graphics (at all) so, please, when I say "best coachwork" don't think of a Rolls Royce, an old Skoda would be a better image.
I took another step forward when I discovered the package 'CGI::Application', which is an integration of the above packages, 'CGI' and 'HTML::Template' with some glue to make things even easier. I think this is a must for every Perl CGI developer.
These packages help you modelling your CGI application as a finite state machine (FSM) where data submitted by the user plays the role of inputs, HTML pages are the output and Perl code implements most of the FSM logic (except for the state transition function).
As we all know, HTTP is a stateless protocol so we can't rely on it to keep state information. 'CGI::Application' found its own way to workaround this problem by putting portions of the state transition function in the HTML page sent to the client which, in turn, will pass the next state information back to the server (using the 'GET' or 'POST' method) when the user click on the 'Submit' button.
Lets now enter into details.
The script which is executed when the browser request the application page (let's name it 'example.cgi') is always the same and it's as simple as:
#!/usr/bin/perl use example; my $example = example->new(); $example->run();
generally, I put it in the cgi-bin directory and I forget about it.
This script only creates an instance of the CGI application and starts it, the real CGI application lives in a separated Perl package (in this example I named it 'example.pm') which must 'set up' the FSM and defining the actions to be taken in every state. Let's have a look at its first section:
package example; use strict; use warnings; use base 'CGI::Application'; sub setup{ my $self = shift; $self->start_mode('login'); $self->mode_param('newState'); $self->run_modes(['login', 'authentication', 'showResult']); }
The first thing worth nothing is the "use base 'CGI::Application'" directive: this tells us our 'example' package inherits from 'CGI::Application' and is probably going to override some of its content. Actually, that's exactly what it does, it overrides the 'setup()' sub to provide its own configuration procedure. Let's have a look now at what 'setup()' actually does.
First of all I must make a confession: the FSM model is an idea of mine, nowhere in the 'CGI::Application' documentation it is ever used as a paradigm. As a consequence, this package identifiers don't reflect in anyway the FSM jargon. Let's now look line by line at the above fragment:
$self->start_mode('login');
by calling the 'start_mode()' sub we define our FSM's first state and say that it all starts with a login.
$self->mode_param('newState');
this is to tell 'CGI::Application' package to look for 'next state' information in a CGI parameter named 'newState'. It will likely receive this information through 'GET' or 'POST' methods when the user press the 'Submit' button (it's in an hidden 'input' field named 'newState'). Anyway, that's not our application's business, it's only expected to know where is that information, not how to get it.
$self->run_modes(['login', 'authentication', 'showResult']);
This is a mapping between FSM states and Perl subs that have to be executed whenever any state is entered. Basically, this line says "when state 'login' is reached execute sub 'login()', when it comes to state 'authentication' execute sub 'authentication()' and when the state is 'showResult', please run 'showResult()'. Think of these three subs as callbacks that gets automatically called at the right time.
That's all: our CGI framework is ready.
Please note that even if most of the job is still to be done, the CGI part is finished. At this point we only have to implement the required functionalities (which is of course the hard job) but we can write the code the same way we'd wrote it if we were working on the usual Perl console application, the CGI part of the job is really over.
Before going on, let's write a couple of things about what has to do the CGI application I'm going to implement. Basically it has to add up a couple of (small) integers. To put a bit of salt in it, I've also inserted a login procedure to protect access to this sort of calculator.
Let's have a look at what to do when the state is 'login', i.e. a user has just reached our application (remember, we said this is the state our application is in when it is started).
sub login{ my $self = shift; my $HTMLPage = $self->load_tmpl('example.tmpl'); $HTMLPage->param('showSection_login' => 1); return $HTMLPage->output; }
This is all about one of the modules I talked about some kilobytes of text ago: HTML::Template. With a couple of lines we load the HTML template file, enable the right HTML section and give it to CGI::Application, that knows what to do to make that page appear on the user browser. If you can't understand what I'm saying about HTML templates read the package documentation.
Let's go on and have a look at what to do after the state transition to 'authentication' state:
sub authentication{ my $self = shift; my $query = $self->query(); my $user = $query->param('user'); my $password = $query->param('password'); my $HTMLPage = $self->load_tmpl('example.tmpl'); if (isUserAuthenticated($user, $password)){ $HTMLPage->param('showSection_welcomePage' => 1); $HTMLPage->param('user' => $user); } else{ $HTMLPage->param('showSection_loginFailed' => 1); } return $HTMLPage->output; } sub isUserAuthenticated{ my ($user, $password) = @_; return ($user =~ /^max$/io && $password =~ /max/o ? 1 : 0); }
This code snippet is useful to see how the other beloved Perl package ('CGI') works when integrated in 'CGI::Application'. The 'authentication()' procedure is used to authenticate the user, so the first thing to do is reading the user input ('user' and 'password' input fields), which is exactly what the first three lines are there for.
Going on through the code we meet another 'load_tmpl()' call: please note that always the same file template is loaded. When using HTML::Template I found useful to have only one template file containing all the HTML code required by the application. By using tag '<TMPL_IF NAME="TAG_NAME">' in the HTML code (see 'HTML::Template documentation') and the corresponding '$HTMLPage->param('TAG_NAME' => 1)' in the Perl code it's possibile to switch on and off whole chunks of HTML code. This let us switch on the right HTML code to be shown to the user, according to the result returned by 'isUserAuthenticated()'.
We have now reached the end of our CGI application: the 'showResult' state. Here is the last code snippet:
sub showResult{ my $self = shift; # Get input submitted in the previous step (i.e. numbers to be added up) my $query = $self->query(); my $number1 = $query->param('number1'); my $number2 = $query->param('number2'); my $HTMLPage = $self->load_tmpl('example.tmpl'); $HTMLPage->param('showSection_result' => 1); $HTMLPage->param('number1' => $number1); $HTMLPage->param('number2' => $number2); $HTMLPage->param('result' => $number1 + $number2); return $HTMLPage->output; } 1; # this is a module, do you remember? It must return something
Here again, we have the first three lines that read user input (the numbers to be added) followed by some lines that load the HTML template file, turn on the right HTML section and fill in the parameters needed: the ones showing the numbers to be summed and the one showing the result.
For a better understanding of the Perl code, it's useful to have a quick look at the mysterious HTML template file I've told before:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head><title>Example</title></head> <body> <TMPL_IF NAME="showSection_login"> <!-- this is the page section shown on the first access to 'example.cgi' --> <form method="post" action="example.cgi"> <p><input type="hidden" name="newState" value="authentication"/></p> <p>user: <input type="text" name="user"/></p> <p>password: <input type="password" name="password"/></p> <p><input type="submit" name="action" value="Login"/></p> </form> </TMPL_IF> <TMPL_IF NAME="showSection_welcomePage"> <!-- this is the page section shown after a successfull login --> <h3>Login succeeded: welcome <TMPL_VAR NAME="user">!</h3> <p>You can now perform the task this web application was created for: adding up small integers</p> <form method="post" action="example.cgi"> <p><input type="hidden" name="newState" value="showResult"/></p> <p>first number: <input type="text" name="number1"/></p> <p>second number: <input type="text" name="number2"/></p> <p><input type="submit" name="action" value="Calculate"/></p> </form> </TMPL_IF> <TMPL_IF NAME="showSection_result"> <!-- this is the page section containing the operation result --> <h3>The sum of <TMPL_VAR NAME="number1"> and <TMPL_VAR NAME="number2"> is <TMPL_VAR NAME="result"></h3> </TMPL_IF> <TMPL_IF NAME="showSection_loginFailed"> <!-- this is the page section shown on login failure --> <h3>Login failure: bad username or password</h3> </TMPL_IF> </body> </html>
In the above fragment there are the HTML sections that gets enabled in turn, by the Perl code, when the corresponding state is reached. I could have used many different HTML template files, one for each section, but doing this would have forced me to duplicate the code outside the tags '<body>' and '</body>'. Just think how bad would be to maintain a big CSS embedded in the 'head' section of the HTML template file when it's replicated through a lot of template files: it looks to me much better having only one 'head' section and different HTML chunks of code that gets switched on and off.
That's all. Obviously, by looking at 'CGI::Application' documentation you'll find a lot of more things that helps you when developing CGI applications, and that's exactly what I suggest that you should do before starting using this package.