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
Using CGI::Application
By Jesse Erlbaum
June 5, 2001
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.
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()
- Configures our run-mode map, and other application settings.
show_search_form()
- Run-mode "mode_1". Returns the HTML search form.
show_results_list()
- Run-mode "mode_2". Based on the contents of the search form, it
finds matching items in the database. The results are formatted
in HTML and returned. A button is provided for each matching item,
allowing the Web user to select one item by clicking on it. Clicking
on an item sets the value of form parameter "rm" to "mode_3" and
sets the value of another form parameter (e.g.: "item_id") to the
unique identifier for the selected item.
show_widget_detail()
- Run-mode "mode_3". Based on the value of the form parameter "item_id",
this method retrieves all the details about the specified item from
the database. These details are formatted as HTML and returned by
this run-mode method.
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.
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 :
- Download
CGI::Application
- http://www.cpan.org/authors/id/J/JE/JERLBAUM/
- CGI Application Mailing List
- Send email to:
cgiapp-subscribe@lists.vm.com
CGI.pm
-
http://stein.cshl.org/WWW/software/CGI/cgi_docs.html
HTML::Template
-
http:/search.cpan.org/search?query=HTML::Template&mode=all
- Apache/
mod_perl
- http://perl.apache.org/
Return to Related Articles from the O'Reilly Network .
Perl.com Compilation Copyright © 1998-2003 O'Reilly & Associates, Inc. |