Published on Perl.com http://www.perl.com/pub/a/2001/06/05/cgi.html
See
this if you're having trouble printing code examples
The Common Gateway Interface (CGI) may be viewed by some as "less than glamorous", but it is the workhorse of Web-based application development. For what CGI lacks in buzzword compliance, it more than makes up for in reliability, flexibility, portability, and (perhaps most important of all) familiarity!
CGI::Application
builds upon the bedrock of CGI, adding a structure for
writing truly reusable Web-applications. CGI::Application
takes what
works about CGI and simply provides a structure to negate some of the
more onerous programming techniques that have cast an unfavorable
light upon it.
CGI::Application
code is so universal and non-proprietary that it works
exceedingly well on any operating system and Web server that supports
Perl and CGI. As you shall see, the CGI::Application
structure even
makes it possible for authors to distribute, for the first time, fully
functional and sophisticated Web-applications via CPAN.
CGI::Application
The most significant contribution of CGI::Application
is the formal
structure of "run-modes." A run-mode generally refers to a single
screen of an application. All sophisticated Web applications feature
multiple screens (or "pages"). For instance, an application to search
through a database might feature a search form, a list of results and a
detail of a single record. Each one of these three screens is part of a
whole application.
Different programmers have devised different systems for managing these run-modes. Too many Web applications still look like huge IF-THEN-ELSE blocks, containing each run-mode in the enclosure of one conditional state. Often, these conditionals try to divide the application state by looking for the presence of various form variables. For instance, if a search field is present, show the list of results - otherwise, show the search form:
my $query = CGI->new();
print $query->header();
if (my $search_term = $query->param("search_term")) {
# ...30 lines of code to run a search
# and print the results as HTML
} else {
# ...15 lines of code to display
# the search form
}
It is code such as this that has given CGI a bad name! It is barely structured and easily broken by even small changes in functionality.
The most savvy programmers quickly realized that run-modes are a
specific thing that must be directly managed, and the most succinct
way to determine the run-mode is to explicitly set it. Some systems,
such as ASP, HTML::Mason
, Cold Fusion and JSP attempt to manage these
run-modes by having one physical document for each run-mode. This has
the effect of spreading the code for a single application over at least
as many files as there are run-modes! Taking the run-modes out of
context by breaking them into separate files solves the state
management problem at the cost of creating all sorts of new problems,
not the least of which is the management of code assets. These
run-modes are, after all, all part of the same application.
CGI::Application
provides another solution to the run-mode management
problem by providing two core facilities. First, CGI::Application
designates a single specific HTML form input as a "Mode Parameter".
This Mode Parameter is used to store (and retrieve) the current
run-mode of your application. The value of a run-mode is a simple text
scalar. CGI::Application
reads the value of this Mode Parameter and
acts as a traffic cop, directing the application operation accordingly.
Second, CGI::Application
maps each run-mode to a specific Perl
subroutine. Each subroutine, referred to as a "Run-Mode Method",
implements the behavior of a single run-mode. All of your code,
including all your run-mode methods and the mapping table between
run-modes and subroutines, is stored in a single file. This file is a
Perl module, referred to as your "Application Module".
Your Application Module is a sub-class of CGI::Application
. In fact,
CGI::Application
is never intended to be used directly.
CGI::Application
is referred to by object-oriented enthusiasts as an
"abstract class", and is only used via inheritance. To implement
inheritance from CGI::Application
, put the following code at the top of
your Application Module:
package Your::Web::Application;
use base 'CGI::Application';
This code gives a name to your application (in this case,
"Your::Web::Application
"), and causes CGI::Application
to be
designated as the parent class. This parent class implements a number of
methods that will provide the necessary infrastructure for your
application. Some of the methods are expected to be called by your code
to perform functions or set properties. Other inherited methods are
expected to be implemented in your code, to provide the functionality
specific to your application.
The map between run-modes and run-mode methods is defined in the setup() method. The setup() method is a method that you are expected to override in your Application Module by implementing a setup() subroutine. It is in your setup() subroutine that you define the map between run-modes and run-mode methods. Think of this map as the definitive list of things your application can do. If you ever add a function to your Web application, then you will amend this map to include your new run mode.
This run mode map is defined in your setup() method by using the
run_modes() method provided by CGI::Application
. The run_modes() method
is an instance method that takes, as arguments, an associative array
of run-modes as keys and run-mode method names as values (Note:
CGI::Application
version 1.3 is used in all our examples). To set up
our prototypical database search application with three run-modes, this
is how our code might look:
package WidgetView;
use base 'CGI::Application';
sub setup {
my $self = shift;
$self->run_modes(
'mode_1' => 'show_search_form',
'mode_2' => 'show_results_list',
'mode_3' => 'show_widget_detail'
);
$self->start_mode('mode_1');
$self->mode_param('rm');
}
That's it! The setup method receives an instance of your application
class ($self) as an argument. When you call run_modes() you are setting
the run-modes for this instance, so you use the object-oriented
indirect ("->
") operator. The inherited start_mode() method tells
CGI::Application
which mode to default to, if no mode is specified (as
is the case when the application is first called). The inherited
mode_param() method specifies the name of the HTML form parameter that
will hold the run-mode state of the application from request to
request.
What we have done here is set up an application called "WidgetView" with three run-modes, creatively named "mode_1", "mode_2" and "mode_3". These run-modes map to three as-yet-unwritten subroutines, respectively show_search_form(), show_results_list() and show_widget_detail(). The mode parameter is set to "rm" (the default), and the first mode of operation will be "mode_1".
The run-mode method subroutines will contain the bulk of your code. These run-mode methods each implement the functionality for a particular run-mode. As we mentioned earlier, run-modes loosely translate into screens. As such, your run-mode methods will be responsible for setting up the HTTP and HTML output to be sent back to the requesting Web browser.
The most critical thing to remember about run-mode methods is that they
should never print() anything to STDOUT. The inherited CGI::Application
run() method is singularly responsible for actually sending all HTTP
headers and HTML content to the Web browser. Your run-mode method is
called by the run() method, and your code is expected to return a
scalar containing all your HTML content. If you send anything to
STDOUT, it will cause your application to malfunction. Symptoms of this
type of mistake are typically content preceding HTTP headers, or HTTP
headers appearing more than once in the output. If you see this, then you
have probably tried sending output to STDOUT.
Your run-mode method will invariably need to interact with the CGI
query to retrieve (and set) form parameters. CGI::Application
does not
attempt to provide this basic functionality. Instead, CGI::Application
utilizes Lincoln D. Stein's superb CGI.pm
module for all interactions
with the CGI query. Becoming expert in CGI.pm
will greatly enhance your
mastery of CGI::Application
. CGI::Application
gives you access to the
CGI.pm
query object by way of the inherited query() method. Once you
retrieve the CGI.pm
query object via the query() method, you may
interact with it as required.
For our first run-mode ("mode_1") we have specified what the run-mode method show_search_form() should be called. The purpose of this run-mode is to display the search form when the user first enters the application. Our run-mode method might look something like this:
sub show_search_form { my $self = shift; # Get the CGI.pm query object my $q = $self->query(); my $output = ""; $output .= $q->start_html(-title => "Search Form"); $output .= $q->start_form(); # Build up our HTML form $output .= "Search for Widgets: "; $output .= $q->textfield(-name => 'search_term'); $output .= $q->submit(); # Set the new run-mode, when the user hits "submit" $output .= $q->hidden(-name => 'rm', -value => 'mode_2'); $output .= $q->end_form(); $output .= $q->end_html(); return $output; }
As you can see, this subroutine is straight-forward. The specified
run-mode method is called in an object-oriented context ($self). We
retrieve the CGI.pm
query object via our Application Module's query()
method (inherited from CGI::Application
). The HTML form we create
should be familiar to anybody who has used CGI.pm
. When we
have completely built up our $output, we return it (as opposed to
printing it to STDOUT).
There is only one bit of "magic" going on here, and that is our hidden
form variable "rm". This is the method by which a CGI::Application
gets
from one run-mode to another. In the case of this run-mode (based on
the desired functionality of our application), there is only one place
we can go, and that is to "mode_2" - the list of matching results. If
the "mode_1" run-mode allowed us to do more than one thing (for
instance, to add a new Widget), then we would have to have two buttons on
this screen with each set having a different value for the form variable,
"rm".
How you go about setting that variable is up to you. For
instance, you could have multiple HTML forms, or you could use
JavaScript. CGI::Application
imposes no restrictions on how the
run-mode parameter gets set. It only cares that it is set, and leaves
the logistics up to the application developer. Once the run-mode
parameter is set, CGI::Application
provides all the run-mode state
management necessary to direct your application to the proper
subroutine.
CGI::Application
, by default, will return all content as MIME type
"text/html". This is set by the HTTP headers. If you wish to set a
different MIME type, manipulate a cookie or perform a HTTP redirect,
then you will need to change the default HTTP headers. This is done by using
two inherited CGI::Application
methods: header_type() and
header_props(). Refer to CGI::Application
's perldoc for details on
their usage.
There is one final piece in the CGI::Application
architecture, and that
is the "Instance Script". So far, we have talked extensively about the
Application Module, but we have not yet explained exactly how the
Application Module gets used! This is where the Instance Script comes
in.
In traditional CGI programming, we might have a file, myapp.cgi
,
which is requested by a Web browser. The Web server (based on its
configuration) will treat this file as a program, and return the output
of its execution (as opposed to its content). In traditional CGI, this
file would contain all your application code, and it would be quite
lengthy. Using CGI::Application
, we have put all our code in our
Application Module, instead. This means that the actual file executed
by the Web server can be completely empty of application-specific code!
As a matter of fact, for our prototypical "WidgetView" application,
what follows is the entirety of widgetview.cgi
:
#!/usr/bin/perl -w use WidgetView; my $app = WidgetView->new(); $app->run();
It is that simple! The file, widgetview.cgi
, is referred to as an
"Instance Script" because it manages a single "instance" of your
Application Module. As long as WidgetView.pm
is in Perl's search path
(@INC), this Instance Script will run your entire Web application.
In our prototypical WidgetView application, we have all the essential
components of a complete CGI::Application
.
Our Application Module, WidgetView.pm
, may reside anywhere in the
server's file system, provided it is within Perl's search path. It is
recommended that your Application Module be placed outside the Web
server's public document space, so that its contents are not accessible
directly via the Web server.
The Application Module in our example contains four subroutines:
setup()
show_search_form()
show_results_list()
show_widget_detail()
Our Instance Script, widgetview.cgi
, resides within the Web server's
public document space. It is configured to be treated as a CGI
application. As long as your Web server supports CGI and Perl, your Web
application based on CGI::Application
will operate as you expect.
WidgetView.pm
does not require an Apache Web server - in fact, it will
run equally well on any CGI-compatible server, including Microsoft's
"IIS" or Netscape's "iPlanet" server, regardless of operating system.
Naturally, WidgetView will run exceedingly well on Apache/mod_perl
servers, as CGI::Application
adheres to very clean Perl programming
standards. CGI::Application
was designed, from the ground up, to run in
full strict mode without throwing any warnings.
The concepts presented in this article should provide you with a
starting point for using CGI::Application
as the foundation of your Web
application development. There are many advanced concepts that
complete the CGI::Application
picture, a few of which I will endeavor
to summarize here.
A tremendous potential for reusability is created through the structure
of Instance Scripts. A single Application Module can be used by
multiple Instance Scripts. Consider the potential for writing a Perl
module and using it multiple times within a single project, across
projects or even across organizations! For the first time, high-level
functionality for the Web can be encapsulated in a single Perl module
and distributed. If you are a CPAN author (or interested in becoming
one), you could create a Web application and distribute it via CPAN in the same way CGI::Application
, itself, is distributed!
Instance Scripts also have the capability to set instance-specific
properties. As a result, the Instance Script becomes a sort of
"configuration file" for your Application Module. The new() method
(inherited from CGI::Application
) has the capability to allow you to
set variables that you may utilize in your Application Modules, via
the inherited param() method. As a simple example, you could write a
mail-form application that takes instance parameters such as the
address to which the form contents should be e-mailed. Multiple Instance
Scripts, all referring to the same Application Module, could each
specify a different e-mail recipient or a different form. Refer to the
CGI::Application
perldoc for the usage details on the new() and param()
methods.
CGI::Application
is designed to support code reuse via inheritance.
Application Modules could be devised to provide project-wide
functionality. Specific applications could then inherit from your
custom parent class instead of directly from CGI::Application
. For example, consider
the possibility of having each of your applications load
configuration data from a database, or set specific run-time
properties. Your parent class Application Module might implement a
cgiapp_init() method, which would allow for these types of inherited
behaviors. Refer to CGI::Application
's perldoc for specific usage of
the cgiapp_init() method.
HTML::Template
At my company, Vanguard Media, CGI::Application
is one part of a larger
development strategy. One of the principle guiding forces of our
application strategy is the maximum separation of the HTML GUI (Graphic
User Interface) from the underlying application code. We have found
that the best Perl programmers are rarely the best HTML designers, and
the best HTML designers are rarely the best Perl programmers. It is for
this reason that the separation of these two elements is arguably the
most beneficial design decision you can make when devising an
application architecture.
To this end, Sam Tregar's excellent HTML::Template
module is utilized.
HTML::Template
allows external "template files" to be created for each
screen in our applications. These template files contain 99 percent pure HTML,
with a very small additional syntax for including scalar variables,
loops and conditional blocks of data, set by the calling Run-Mode
Method.
HTML::Template
is so fundamental to our development strategy that
special hooks have been built into CGI::Application
to support its use!
Refer to the CGI::Application
perldoc for usage details of the
inherited tmpl_path() and load_tmpl() methods.
A question that frequently comes up on the CGI::Application
mailing
list is how best to implement login security and session management.
Experience has taught me that these are elements that are best
excluded from your application code, and pushed into a lower layer of
your Web server.
If you are using the Apache Web server, and are interested in
implementing login security and session management, I encourage you to
check out the various Apache::Auth*
modules on CPAN. These modules tie
into the "Authentication" and "Authorization" phases of the request.
This code runs long before your CGI applications are called.
There are two primary advantages in placing your sessions and security code in this layer. First, your security will work for all documents, not just Perl applications. Even static HTML documents will be protected by this system. Second, putting sessions and security in this layer will avoid an architecture where programmers have to include special code at the start of their applications to participate in the sessions and security system.
I hope you enjoyed reading this article. The following references
should help you further explore the use of CGI::Application
:
CGI::Application
cgiapp-subscribe@lists.vm.com
CGI.pm
HTML::Template
mod_perl
Perl.com Compilation Copyright © 1998-2003 O'Reilly & Associates, Inc.