Practical 14

An example web-based CGI application

Introduction

The previous two chapters described the NCBI C++ Toolkit's CGI and HTML classes, with an emphasis on their independence from one another. In practice however, a real application must employ both types of objects, with a good deal of inter-dependency.

Program description

The car.cgi program presents an HTML form for ordering a custom color car with selected features. The form includes a group of checkboxes (listing individual features) and a set of radio buttons listing possible colors. Initially, no features are selected, and the default color is black. Following the form, a summary stating the currently selected features and color, along with a price quote, is displayed. When the submit button is clicked, the form generates a new query string (which includes the selected features and color), and the program is restarted.

The program uses a CHTMLPage object with a template file (car.html) to create the display. The template file contains three <@tag@> locations, which the program uses to map CNCBINodes to the page, using the AddTagMap() method. Here is an outline of the execution sequence:

Create an instance of class CCar named car.

Load car with the color and features specified in the query string.

Create a CHTMLPage named page.

Generate a CHTML_form object using the features and color currently selected for car, and map that HTML form to the <@FORM@> tag in page.

Generate the summary statement and save it in a CHTMLText node mapped to the <@SUMMARY@> tag.

Generate a price quote and save it in a CHTMLText node mapped to the <@PRICE@> tag.

Output page and exit.

The CCar created in step 1 initially has the default color (black) and no features. Any features or colors specified in the query string with which the program was invoked are added to car in step 2, prior to generating the HTML display elements. In step 4, the form element is created using the set of possible features and the set of possible colors. These sets of attributes are stored as static data members in an external utility class, CCarAttr. Each feature corresponds to a CHTML_checkbox element in the form, and each color corresponds to a CHTML_radio button. The selected color, along with all currently selected features, will be displayed as selected in the form.

The summary statement uses a CHTML_ol list element to itemize the selected features in car. The price is calculated as CCar::m_BasePrice plus an additional $1000 per feature. The submit button generates a fresh page with the new query string, as the action attribute of the form is the URL of car.cgi.

Program design: Distributing the work

The program uses three classes: CCar, CCarAttr, and CCarCgi. The CCar class knows nothing about HTML nodes or CGI objects - its only functions are to store the currently selected color and features, and compute the resulting price:

class CCar
{
public:
CCar(unsigned base_price = 12000) { m_BasePrice = base_price; }
// Mutating member functions
void AddFeature(const string& feature_name);
void SetColor(const string& color_name);
// Access member functions
bool HasFeature(const string& feature_name) const;
string GetColor(void) const;
string GetPrice(void) const;
const set<string>& GetFeatures() const;
private:
set<string> m_Features;
string m_Color;
unsigned m_BasePrice;
};

Instead, the CCar class provides an interface to all of its data members, thus allowing the application to get/set features of the car as needed. The static utility class, CCarAttr, simply provides the sets of possible features and colors, which will be used by the application in generating the HTML form for submission:

class CCarAttr {
public:
CCarAttr(void);
static const set<string>& GetFeatures(void) { return sm_Features; }
static const set<string>& GetColors (void) { return sm_Colors; }
private:
static set<string> sm_Features;
static set<string> sm_Colors;
};

Both of these classes are defined in a header file which is #include'd in the *.cpp files. Finally, the application class does most of the actual work, and this class must know about CCar, CCarAttr, HTML, and CGI objects. The CCarCgi class has the following interface:

class CCarCgi : public CCgiApplication
{
public:
virtual int ProcessRequest(CCgiContext& ctx);
private:
CCar* CreateCarByRequest(const CCgiContext& ctx);
void PopulatePage(CHTMLPage& page, const CCar& car);
static CNCBINode* ComposeSummary(const CCar& car);
static CNCBINode* ComposeForm (const CCar& car);
static CNCBINode* ComposePrice (const CCar& car);
static const char sm_ColorTag[];
static const char sm_FeatureTag[];
};

The source code is distributed over three files:

car.hpp http://www.ncbi.nlm.nih.gov/bookshelf/br.fcgi?book=toolkit&part=ch_demo#ch_demo.car.hpp

car.cpp http://www.ncbi.nlm.nih.gov/bookshelf/br.fcgi?book=toolkit&part=ch_demo#ch_demo.car.cpp

car_cgi.cpp http://www.ncbi.nlm.nih.gov/bookshelf/br.fcgi?book=toolkit&part=ch_demo#ch_demo.car_cgi.cpp

The CCar and CCarAttr classes are defined in car.hpp, and implemented in car.cpp. Both the class definition and implementation for the CGI application class are in car_cgi.cpp. With this design, only the application class will be affected by changes made to either the HTML or CGI class objects. The additional files needed to compile and run the program are:

car.html http://www.ncbi.nlm.nih.gov/bookshelf/br.fcgi?book=toolkit&part=ch_demo&rendertype=figure&id=ch_demo.F1

Makefile.car_app http://www.ncbi.nlm.nih.gov/bookshelf/br.fcgi?book=toolkit&part=ch_demo#ch_demo.Makefile.car_app

Comments