cgi|xml| Program
Brief introduction
The popularity of Perl has a direct relationship with the flourishing of the Internet. In the early days of internet development, it was found that only static HTML documents could not generate an effective interactive environment, so the concept of Common Gateway Interface (CGI) was introduced. Perl's powerful features and easy to expand features make it the most natural choice for developing CGI applications, and thus quickly become the preferred language for CGI scripts. CGI itself is not perfect. But thanks to the favor of many developers, CGI is still widely used, and there is no indication that it will "retire" in the near future.
Cgi::xmlapplication provides a module based on XML that can be used as a traditional CGI script. A typical cgi::xmlapplication script consists of three parts: a small executable script that provides access to the application, a logical module that implements various management methods, and possibly one or more XSLT stylesheets depending on the state of application. An XSLT stylesheet can translate the results returned by a module into a format that the browser can display to the user.
Here we will briefly introduce the application of cgi::xmlapplication by example.
Example 1:cgi XSLT Gateway
Cgi::xmlapplication assumes that the design and developers involved in a project use the XSLT stylesheet to separate the logic and representations of the application, so that the separation is straightforward and does not affect the project. Developers can simply make Setstylesheet return the location of the XSLT style sheet that matches the current application state. The conversion of the established DOM tree, the transfer of XSLT parameters to the transformation engine, and the transfer of content to the browser after conversion are transparent to the user.
To focus on this separation, our first example is not a traditional Web application, but rather a generic XSLT gateway that can be added to the server's Cgi-bin, transforming the entire XML content directory tree into a browser-compliant format, all of which for users, The style sheet and the author of the document are also transparent.
The first step is to establish a CGI script that connects the client's request and application. We want XML documents to be easy to navigate through URLs and to make hyperlinks between these documents very intuitive. Therefore, we will create a CGI script without an extension so that it can be used as a node in the URL path, and all content on the right side of the node will be interpreted in the context of the virtual document that contains the XML content. In this case, we call the CGI a stylesheet selector.
Use strict;
Use Lib '/path/to/secure/webapp/libs ';
Use Xslgateway;
Use CGI QW (: Standard), my $q = Cgi->new ();
My%context = ();
My $gateway _name = ' stylechooser ';
After loading the appropriate modules and setting some variables that are valid throughout the scope of the script, we begin to add some fields to the%context that is passed to the class that handles the application logic. In this application, we only transfer the requested URL (request entry) to the right of the script file path and the style key that contains the data stored in the query parameter style.
$context {REQUEST} = $q->url (-path => 1);
$context {REQUEST} =~ s/^ $gateway _name\/?//;
$context {REQUEST} | | = ' index.xml ';
$context {Style} = $q->param (' style ') if $q->param (' style ');
Finally, we create an instance of the Xslgateway logical class and use%context as the unique parameter by invoking its run method to process the request.
My $app = Xslgateway->new ();
$app->run (%context);
The CGI script is done. Here we create the Xslgateway module that completes most of the work:
Package Xslgateway;
Use strict;
Use VARs QW (@ISA);
Use cgi::xmlapplication;
Use Xml::libxml;
@ISA = QW (cgi::xmlapplication);
As I mentioned in the introduction, Cgi::xmlapplication works through event invocations: The execution of a given method in an application class relies on the input of a specified field (usually the name of the button used to submit the form). ), you must perform two invocation methods: Selectstylesheet and Requestdom methods.
Selectstylesheet returns the full file system path for the XSLT style sheet. For simplicity's sake, we assume that the stylesheet will be saved in a single table of contents. We can increase the flexibility of the system by providing additional stylesheets through the $context->{style} domain.
Sub Selectstylesheet {
my $self = shift;
my $context = shift;
My $style = $context->{style} | | ' Default ';
My $style _path = '/opt/www/htdocs/stylesheets/';
Return $style _path. $style. '. Xsl ';
}
Next, we need to create a Requestdom method that returns the Xml::libxml DOM expression for the transferred XML document. Since our gateway applies only to static files, we need to parse the document using Xml::libxml and return the result tree.
Sub Requestdom {
my $self = shift;
my $context = shift;
My $xml _file = $context->{request} | | ' Index.xml ';
My $doc _path = '/opt/www/htdocs/xmldocs/';
My $requested _doc = $doc _path. $xml _file;
My $parser = xml::libxml->new;
My $doc = $parser->parse_file ($requested _doc);
return $doc;
}
At this point, our CGI scripts are safe to run safely in the server's Cgi-bin directory and upload some XML documents and one or two XSLT stylesheets in some of the appropriate directories. Now we can start to test the results of our work. A request to Http://localhost/cgi-bin/stylechooser/mydocs/somefile.xml will enable the Internet server to select from the/opt/www/htdocs/xmldocs/directory mydocs/ Somefile.xml file, convert the file using the style sheet default.xsl in/opt/www/htdocs/stylesheets/and transfer it to the customer.
If necessary, we can expand this basic framework, for example, you can select the CGI script in the style sheet to add some lookup components, select the appropriate style sheet, you can set up or read HTTP cookies, modify the site.
Example 2: A simple shopping system
In this example, we will use cgi::xmlapplication to create a simplified Web application, shopping system.
As with the previous example, the Cgi-bin-related parts of this application are still very small. All we need to do is initialize the CustomerOrder application class and invoke its run () method. This time, we will cgi.pm VARs as the params domain of%context:
Use strict;
Use CGI QW (: Standard);
Use Lib '/path/to/secure/webapp/libs ';
Use CustomerOrder;
My $q = Cgi->new ();
My%context = ();
$context {PARAMS} = $q->vars;
My $app = Customerorder->new ();
$app->run (%context);
In this example, we assume that the product information in the application is stored in the relational database. Product list is not too long, so that we do not appear in the application of multiple screens to show the relevant information of the trouble: the user input the number of products ordered the main data input screen, display the purchase order content and the total price of the selected items confirmation screen, Displays the prompt that the order has been processed. For simplicity's sake, we don't have issues with shipping and financial data input.
Package CustomerOrder;
Use strict;
Use VARs QW (@ISA);
Use cgi::xmlapplication;
Use Xml::libxml::sax::builder;
Use xml::generator::D bi;
Use DBI;
@ISA = QW (cgi::xmlapplication);
After loading the necessary modules and defining the classes inherited from Cgi::xmlaplication, we started creating event calls in the application that are related to various states. First, we must register these events by creating the Registerevents () method. In this case, we will register the Order_confirm and Order_send methods, which set the Screenstyle domain in%context. Later, we will use this property to define which of the three XSLT style sheets should be used when displaying the data for the client.
It should be noted that these events will be mapped to the actual subroutine that implements them, and the subroutine's naming rule is the event_< event name, for example, the Order_confim event is executed by Event_order_confim. Also, it should be noted that the choice of events is based on Cgi::xmlapplication's ability to find a table parameter with the same name as the registered event. For example, to perform a order_confirm event, a table component must contain a table field with a order_confirm name that submits a non-empty value. -->
# Registration and event calls for events
Sub Registerevents {
return QW (order_confirm order_send);
}
Sub Event_order_confirm {
My ($self, $context) = @_;
$context->{screenstyle} = ' order_confirm.xsl ';
}
Sub Event_order_send {
My ($self, $context) = @_;
$context->{screenstyle} = ' order_send.xsl ';
}
If no other event is requested to execute, the Event_default is performed by default. In this case, we only use it to set the Screenstyle field to an appropriate value.
Sub Event_default {
My ($self, $context) = @_;
$context->{screenstyle} = ' order_default.xsl ';
}
Each request executes the Event_init method, and always executes it before other methods, which makes it ideal for initializing parts of the application that are used by other events. In this case, we use it to return the original DOM tree that uses the Fetch_recordset () method to obtain product information from the database.
Sub Event_init {
My ($self, $context) = @_;
$context->{domtree} = $self->fetch_recordset ();
}
Once the State-handler method is complete, we need to perform the necessary selectstylesheet and Requestdom methods.
As in the first example, we assume that all of the applied style sheets are stored in the same directory on the server. What we need to do is return the $context->{screenstyle} value to the specified route and add it to the end.
# app Config and helpers
Sub Selectstylesheet {
My ($self, $context) = @_;
My $style = $context->{screenstyle};
My $style _path = '/opt/www/htdocs/stylesheets/cart/';
Return $style _path. $style;
}
Before we study the requestdom process, we'll look at the Fetch_recordset helper method in detail.
It is important to remember that the work we do is to select information about the products ordered from a relational database, but the data passed to the XSLT processor must be a DOM tree. In this case, instead of programming, we take advantage of Xml::generator::D bi, which generates SAX data from the data that is obtained from executing the SQL SELECT statement. Creating a required DOM tree is an instance of establishing a xml::libxml::sax::builder (which creates a xml::libxml dom tree from a SAX event).
Sub Fetch_recordset {
my $self = shift;
My $sql = ' Select ID, name, price from products ';
My $dbh = Dbi->connect (' dbi:Oracle:webclients ',
' Chico ',
' Swordfish ')
|| Die "database connection couldn ' t
Be initialized: $DBI:: errstr \ n ";
My $builder = Xml::libxml::sax::builder->new ();
My $gen = xml::generator::D bi->new (Handler => $builder,
DBH => $DBH,
RootElement => ' document ',
Queryelement => ' ProductList ',
Rowelement => ' product ');
My $dom = $gen->execute ($sql) | | Die "Error building DOM tree\n";
return $dom;}
The Fetch_recordset method completes another important task, but the DOM tree it returns contains only part of the information we want to send to the customer, we must also get the number of products the user has entered, and a total of the ordered products.
Sub Requestdom {
My ($self, $context) = @_;
My $root = $context->{domtree}->getdocumentelement ();
My $grand _total = ' 0 ';
To take the current order quantity as part of a larger document, we will iterate through all the product elements and add <quantity> and <item-total> child elements to each line. The value of <quantity> can be obtained from the $context->{params} domain.
foreach My $row ($root->findnodes ('/document/productlist/product ')) {
My $id = $row->findvalue (' id ');
My $cost = $row->findvalue (' price ');
My $quantity = $context->{params}->{$id} | | ' 0 ';
My $item _total = $quantity * $cost;
$grand _total + + $item _total;
# Add the order quantity and item totals to the tree.
$row->appendtextchild (' quantity ', $quantity);
$row->appendtextchild (' item-total ', $item _total);
}
Finally, we'll add some meta information about the order by adding a <instance-info> element to the root element with the <order-total> element that contains the total value of the currently selected goods.
$grand _total | | = ' 0.00 ';
My $info = xml::libxml::element->new (' Instance-info ');
$info->appendtextchild (' order-total ', $grand _total);
$root->appendchild ($info);
return $context->{domtree};
}
Attentive readers may have noticed that our very simple application did not do anything practical in the Order_send method. Deciding how to handle this data is the most relevant part of a product ordering application that is related to a specific shopping site.
Conclusion
Cgi::xmlapplication provides a clear, modular way to isolate the content and representations of a system in the programming of a CGI script, and this alone is worth studying. In addition, it allows us to avoid obsessing over some of the details and concentrate on solving the main problems.