home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Perl.com
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

Why CGI::Application?

Table of Contents

Why CGI::Application?

Understanding CGI::Application

Putting It All Together

Conclusions & Advanced Concepts: Where to Go From Here

Resources

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.

Understanding CGI::Application

Run-Modes

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.

Application Modules

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.

Defining Your Run-Mode Map

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".

Creating Run-Mode Methods

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.

HTTP Headers

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.

Instance Scripts

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.

Putting It All Together

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.

A more complete source listing of the WidgetView application can be found here.

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.

Conclusions & Advanced Concepts: Where to Go From Here

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.

Code Reuse

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.

Separating the HTML GUI From Application Code Using 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.

Thoughts on Sessions and Security

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.

Resources

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
Apache/mod_perl
http://perl.apache.org/

Return to Related Articles from the O'Reilly Network .


Library Navigation Links

Perl.com Compilation Copyright © 1998-2003 O'Reilly & Associates, Inc.