简介
与Web服务有关的的一个基本问题是如何创建一个既能够通过基于浏览器的客户端又能够通过编程方式让客户端自动访问应用程序。在本文中,我们将讨论如何利用Perl和XML简单地创建多界面的Web服务。
我们之所以选择Perl和XML,与SOAP、XML-RPC和REST的优缺点无关,也不是为了试图解决哪种工具更适合用来开发Web服务的问题。我们在这里想要说明的是,只要动点脑筋并使用一些Perl模块,就可以创建出实用的而且能够通过多种客户端进行访问的Web服务。
例子━━WebSemDiff:多界面的XML Semantic Diff Web服务
在本篇文章中,我们将建立XML::SemanticDiff模块的一个Web界面。XML::SemanticDiff能够在忽略细节的情况下比较二个XML文档的内容。
阅读本文之前,我建议读者应当对CGI::XMLApplication有个基本的了解。对它有一定的了解会对理解我们本篇文章的内容有所帮助。事实上只要了解一个典型的包括三个部分的CGI::XMLApplication应用程序就够了:连接客户端和应用程序的CGI脚本、处理任务的Perl模块以及将Perl模块中返回的DOM树转换为客户端应用程序能够接受的XSLT样式表。
理解CGI::XMLApplication的基本架构是十分重要的,因为SOAP::Lite模块也使用了相同的架构,开发多客户端访问应用的根本目的在于对这二个模块整合的理解。
首先,我们来看看CGI::XMLApplication和SOAP::Lite用来比较上传到服务器的文件所使用的基本模块:
package WebSemDiff; use strict; use CGI::XMLApplication; use XML::SemanticDiff; use XML::LibXML::SAX::Builder; use XML::Generator::PerlData;
use vars qw( @ISA ); @ISA = qw( CGI::XMLApplication );
|
在导入必要的模块以及声明软件包与CGI::XMLApplication的继承关系后,我们需要实现使浏览器界面工作的方法。
浏览器界面有二种状态:缺省状态是提醒用户上传二个XML文档进行比较,显示比较结果的结果状态(或在比较时出现的错误)。selectStylesheet()方法返回由应用程序生成的DOM树转换成的样式表的路径。在这里我们不对semdiff_default.xsl和semdiff_result.xsl这二个样式表进行详细的讨论。
sub selectStylesheet { my ( $self, $context ) = @_; my $style = $context->{style} || 'default'; my $style_path = '/www/site/stylesheets/'; return $style_path . 'semdiff_' . $style . '.xsl'; } |
缺省情况下,必需的getDOM()方法将返回一个XML::LibXML::Document对象。在向浏览器返回结果前,由selectStylesheet()方法设定的XSLT样式表将对该文档对象进行转换。
sub getDOM { my ( $self, $context ) = @_; return $context->{domtree}; } |
getXSLParameter()方法提供了从类向样式表传送值的一种方式(可以通过<xsl:param>元素获得该值)。在这里,我们只增加所有的请求参数,让样式表来选择相关的域。
sub getXSLParameter { my $self = shift; return $self->Vars; } |
由于缺省状态只是一个不要求应用程序逻辑或特别处理的简单提示,因此我们只需实现对结果状态的访问即可:
# 登录事件和回调事件 sub registerEvents { return qw( semdiff_result ); }
sub event_semdiff_result { my ( $self, $context ) = @_; my ( $file1, $file2, $error ); my $fh1 = $self->upload('file1'); my $fh3 = $self->upload('file2'); $context->{style} = 'result'; |
在为应用程序的状态设置合适的样式后,我们就能够获得包含有上传的XML文档的文件句柄。我们首先检查二个句柄是否存在,如果存在,则转换为二个简单的标量:
if ( defined( $fh1 ) and defined( $fh3 ) ) { local $/ = undef; $file1 = <$fh1> $file2 = <$fh3>; |