简介

Perl的普及与互联网的蓬勃发展有直接的关系。Perl强大的功能 和容易扩充的特性使得它成为开发CGI应用最自然的选择,并由此迅速地成为CGI 脚本的首选语言。CGI本身并非十全十美。但由于得到了众多开发商的青睐,CGI 的应用至今仍然十分广泛,而且没有迹象表明在近期会“退休”。

典型的CGI::XMLApplication脚本包括三部分:一个很小的提供对该应用 程序访问支持的可执行脚本、实现各种管理者方法的逻辑模块、根据应用状态可 能有一个或多个XSLT样式表,XSLT样式表能够将模块返回的结果转化成浏览器可 以向用户显示的格式。

下面我们通过例子来简要地介绍 CGI::XMLApplication的应用。

例1:CGI XSLT网关

CGI::XMLApplication假定,参与一个项目的设计和开发人员使用XSLT样 式表分离应用的逻辑和表示,这样可以使这种分离显得非常直接,也不会对项目 带来影响。开发人员只要能够使setStylesheet返回符合当前应用状态的XSLT样式 表的位置即可。应用建立的DOM树的转换、XSLT参数向转换引擎的传递、转换后内 容向浏览器的传输对用户而言都是透明的。

为了重点说明这种分离,我们 的第一个例子不是传统意义上的Web应用,而是一个通用的XSLT网关,它可以添加 到服务器的cgi-bin中,将整个XML内容的目录树转化为符合请求的浏览器的格式 ,而这一切对于用户、样式表和文档的作者而言也都是透明的。

第一步是 建立连接客户端的请求和应用的CGI脚本。我们希望XML文档能够方便地通过URL浏 览,并使创建这些文档间的超链接非常直观。因此,我们将创建一个没有扩展名 的CGI脚本,以便将它作为URL路径中的一个节点,节点右边的所有内容将在包含 XML内容的虚拟文档环境中进行解释。在这种情况下,我们将CGI称作是样式表选 择者。

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';

在加载合适的模块和设置 一些在整个脚本范围内有效的变量后,我们开始向被传递给处理该应用逻辑的类 的%context中添加一些域。在这个应用软件中,我们只传输要求的指向脚本文件 路径右边的URL(REQUEST条目)和包含有存储在查询参数style中的数据的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');

最后,我们 创建了XSLGateway逻辑类的一个实例,并通过调用其run方法处理请求,将% context作为唯一的参数。

my $app = XSLGateway->new();$app->run(%context);

CGI脚本就完成了。下面我们创建完成大 部分工作的XSLGateway模块:

package XSLGateway;use strict;use vars qw(@ISA);use CGI::XMLApplication;use XML::LibXML;@ISA = qw(CGI::XMLApplication);

象我在简介中 提到的那样,CGI::XMLApplication通过事件调用起作用:应用程序类中一个给定 的方法的执行依赖于一个指定域的输入(一般情况下是用来提交表格的按钮的名 字。),必须执行二种调用方法:selectStylesheet和requestDOM方法。

selectStylesheet返回有关的XSLT样式表的全文件系统路径。为了简单起 见,我们假定样式表将保存在一个单一的目录中。我们可以通过$context-> {STYLE}域提供其他的样式表,从而增加系统的灵活性。

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';}

下一步,我们需要 创建requestDOM方法,该方法将返回被传输的XML文档的XML::LibXML DOM表达式 。由于我们的网关只适用于静态文件,我们需要使用XML::LibXML对文档进行解析 ,并返回结果树。

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;}

至此,我们的CGI脚本已经可以安全地在服务器的cgi-bin目录中安全 地运行了,并在一些适当的目录中上载一些XML文档和一个或二个XSLT样式表。下 面我们就可以开始检验我们的工作成果了。对localhost/cgi- bin/stylechooser/mydocs/somefile.xml的请求将会使互联网服务器 从/opt/www/htdocs/xmldocs/目录中选取mydocs/somefile.xml文件,使 用/opt/www/htdocs/stylesheets/中的样式表default.xsl对该文件进行转换,并 将它传输给客户。

如果需要,我们可以扩充这一基本的框架,例如,可以 在样式表选择CGI脚本程序添加一些查找组件,选择合适的样式表,可以设置或读 取HTTP cookies,对网站进行修饰。

例2:一个简单的购物系统

在 该例子中,我们将使用CGI::XMLApplication创建一个简化的Web应用程序,购物 系统。

与上个例子相同,这个应用程序中与CGI-BIN有关的部分仍然非常 地少。我们所需要作的只不过是初始化CustomerOrder应用类并调用它的run()方 法。这次,我们将CGI.pm中Vars作为%context的PARAMS域:

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);

在这个例子中,我们假定该应用中的产品信息存储在关 系数据库中,产品清单不是太长,使我们在应用中不会出现多屏才能显示相关信 息的麻烦:用户输入订购的产品数量的主要数据输入屏,显示订购单内容和所选 物品总价格的确认屏,显示订单已经处理的提示。为了简单起见,我们在这里没 有涉及送货和财务数据的输入等问题。

package CustomerOrder;use strict;use vars qw(@ISA);use CGI::XMLApplication;use XML::LibXML::SAX::Builder;use XML::Generator::DBI;use DBI;@ISA = qw (CGI::XMLApplication);

在加载必要的模块和定义从 CGI::XMLAplication中继承的类后,我们开始创建应用中与各种状态有关的事件 调用。首先,我们必须通过创建registerEvents()方法注册这些事件。在本例中 ,我们将注册order_confirm 和order_send方法,这二个方法设置%context中的 SCREENSTYLE域。稍后,我们将利用该属性定义在显示客户端的数据时应该使用三 个XSLT样式表中的哪一个。

需要注意的是,这些事件将被映射到实现它们 的实际的子程序中,子程序的命名规则是event_<事件名>,例如, order_confim事件是由event_order_confim执行的。另外,还需要注意的是,各 种事件的选择是由CGI::XMLApplication根据其查找一个与注册事件同名的表格参 数的能力进行的。例如,要执行order_confirm事件,表格组件中必须包含一个提 交非空值的名字为order_confirm的表格域。

# 事件的注册和事件调用

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';}

如果没有请求执行其他的事 件,则缺省地执行event_default。在本例中,我们只使用它将SCREENSTYLE域设 定为一个合适的值。

sub event_default {my ($self, $context) = @_;$context->{SCREENSTYLE} = 'order_default.xsl';}

每次请求都会执行 event_init方法,而且总是在其他方法之前执行它,这使得它非常适合对应用中 被其他事件使用的部分进行初始化。在本例中,我们使用它返回利用 fetch_recordset()方法从数据库中获取的产品信息的、最初的DOM树。

sub event_init {my ($self, $context) = @_;$context->{DOMTREE} = $self->fetch_recordset();}

state-handler方法完成后,我们需要执行必需的 selectStylesheet和requestDOM方法。

与在第一个例子中一样,我们假设 所有的应用的样式表都存储在服务器上相同的目录中。我们所需要作的是返回 $context->{SCREENSTYLE}的值所指定的路线,并添加到末尾。

# app config and helperssub selectStylesheet {my ($self, $context) = @_;my $style = $context-> {SCREENSTYLE};my $style_path = '/opt/www/htdocs/stylesheets/cart/';return $style_path . $style;}

在研究requestDOM处理程序之前,我们先来详细 地研究fetch_recordset helper方法。

需要记住的是,我们要做的工作是 从一个关系数据库中选择所订购产品的有关信息,但传递给XSLT处理器的数据必 须是DOM树。在本例中,我们不通过编程的方法,而是利用XML::Generator::DBI ,它能够从执行SQL SELECT语句得到的数据中生成SAX数据。创建要求的DOM树就 是建立XML::LibXML::SAX::Builder(它从SAX事件中创建XML::LibXML DOM树)的 实例。

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'tbe initialized: $DBI::errstr n";my $builder = XML::LibXML::SAX::Builder->new();my $gen = XML::Generator::DBI->new(Handler => $builder,dbh => $dbh,RootElement => 'document',QueryElement => 'productlist',RowElement => 'product');my $dom = $gen->execute($sql) || die "Error Building DOM Treen";return $dom;}

fetch_recordset方法完成了另一项很重要的任务,但它返回的 DOM树只包含我们想向客户发送信息的一部分,我们还必须获取用户输入的产品数 量,另外,还需要提供一个订购产品的总计。

sub requestDOM {my ($self, $context) = @_;my $root = $context-> {DOMTREE}->getDocumentElement();my $grand_total = '0';

为了将当前的订货数量作为更大的文档的一部分, 我们将遍历所有的产品元素,并在每行中添加和子元素。的值可以从$context- >{PARAMS}域获得。

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);}

最后,我们将增加一些有关订单的元信息 ,方法是在具有元素的根元素中添加一个元素,该元素中包含有当前所选货物的 总价值。

$grand_total ||= '0.00';my $info = XML::LibXML::Element->new('instance-info');$info- >appendTextChild('order-total', $grand_total);$root- >appendChild($info);return $context->{DOMTREE};}

细心的读者可能已经注意到,我们这个非常简单的应用程序在 order_send方法中没有作任何实际的事。决定如何处理这些数据是产品订购应用 程序中与具体的购物网站最有关的部分。

结束语

CGI::XMLApplication在CGI脚本程序的编程中提供了一种清晰的、模块化 的隔离系统的内容和表示的方法,单就这一点,就值得我们对它进行一番研究。 此外,它还可以使我们避免纠缠于一些细节问题,而集中精力解决主要的问题。

【相关推荐】

1. 详解cgi向文本或者数据库写入数据实例代码

2. 分享在IIS上用CGI方式运行Python脚本的实例教程

3. 使用CGI模块建立简单web页面教程实例

4. 分享一个PythonCGI编程的实例教程

5. 什么是CGI?详细介绍Python CGI编程

6. FastCGI 进程意外退出造成500错误

更多相关文章

  1. 详细介绍eclipse关闭XML验证的方法
  2. XML文件使用SAX方法读取的示例代码
  3. XML文件使用DOM方法读取的示例代码
  4. xml实现多渠道接入网站的构架的方法
  5. 四种获取RSS源xml文件的方法
  6. 类TransformBinder将xml解析为xhtml的实例方法
  7. 把SQL Server中的数据导出为XML和Json的方法解析
  8. 用Shell脚本生成XML文件实例详解
  9. android三种操作XML的方法总结

随机推荐

  1. API 23 inputmethodservice.KeyboardView
  2. Android:Android(安卓)Studio配置Kotlin
  3. Form表单组合控件
  4. Android - gravity and layout_gravity
  5. Android N画中画模式
  6. Android中AIDL实现进程通信(附源码下载)
  7. Android(安卓)Studio library R.java生成
  8. Android ListView列表 刷新和加载更多
  9. Android 应用程序背景透明,有标题的初始页
  10. Android Provider 属性