監(jiān)理公司管理系統(tǒng) | 工程企業(yè)管理系統(tǒng) | OA系統(tǒng) | ERP系統(tǒng) | 造價咨詢管理系統(tǒng) | 工程設計管理系統(tǒng) | 甲方項目管理系統(tǒng) | 簽約案例 | 客戶案例 | 在線試用
X 關閉

用Perl和XML輕松開發(fā)多種界面的Web服務

申請免費試用、咨詢電話:400-8352-114

AMTeam.org

用Perl和XML輕松開發(fā)多種界面的Web服務


 
簡介

與Web服務有關的的一個基本問題是如何創(chuàng)建一個既能夠通過基于瀏覽器的客戶端又能夠通過編程方式讓客戶端自動訪問應用程序。在本文中,我們將討論如何利用Perl和XML簡單地創(chuàng)建多界面的Web服務。

我們之所以選擇Perl和XML,與SOAP、XML-RPC和REST的優(yōu)缺點無關,也不是為了試圖解決哪種工具更適合用來開發(fā)Web服務的問題。我們在這里想要說明的是,只要動點腦筋并使用一些Perl模塊,就可以創(chuàng)建出實用的而且能夠通過多種客戶端進行訪問的Web服務。

例子━━WebSemDiff:多界面的XML Semantic Diff Web服務

在本篇文章中,我們將建立XML::SemanticDiff模塊的一個Web界面。XML::SemanticDiff能夠在忽略細節(jié)的情況下比較二個XML文檔的內容。

閱讀本文之前,我建議讀者應當對CGI::XMLApplication有個基本的了解。對它有一定的了解會對理解我們本篇文章的內容有所幫助。事實上只要了解一個典型的包括三個部分的CGI::XMLApplication應用程序就夠了:連接客戶端和應用程序的CGI腳本、處理任務的Perl模塊以及將Perl模塊中返回的DOM樹轉換為客戶端應用程序能夠接受的XSLT樣式表。

理解CGI::XMLApplication的基本架構是十分重要的,因為SOAP::Lite模塊也使用了相同的架構,開發(fā)多客戶端訪問應用的根本目的在于對這二個模塊整合的理解。

首先,我們來看看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的繼承關系后,我們需要實現使瀏覽器界面工作的方法。

瀏覽器界面有二種狀態(tài):缺省狀態(tài)是提醒用戶上傳二個XML文檔進行比較,顯示比較結果的結果狀態(tài)(或在比較時出現的錯誤)。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;
}

由于缺省狀態(tài)只是一個不要求應用程序邏輯或特別處理的簡單提示,因此我們只需實現對結果狀態(tài)的訪問即可:

# 登錄事件和回調事件
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';

在為應用程序的狀態(tài)設置合適的樣式后,我們就能夠獲得包含有上傳的XML文檔的文件句柄。我們首先檢查二個句柄是否存在,如果存在,則轉換為二個簡單的標量:

if ( defined( $fh1 ) and defined( $fh3 ) ) {
local $/ = undef;
$file1 = <$fh1>
$file2 = <$fh3>;

其次,我們創(chuàng)建包含由通過調用compare_as_dom()方法生成的比較結果的DOM樹。將這次調用封裝在一個eval塊中,以確保我們能夠獲得在處理上傳的文檔時發(fā)生的解析錯誤。在稍后,我們將仔細地研究 compare_as_dom()和dom_from_data()方法。

eval {
$context->{domtree} = $self->compare_as_dom( $file1, $file2 );
};

if ( $@ ) {
$error = $@;
}
}
else {
$error = 'You must select two XML files to compare
and wait for them to finish uploading';
}

if ( $error ) {
$context->{domtree} = $self->dom_from_data( { error => $error } );
}

如果二個文檔完全相同,compare_as_dom()返回一個示定義的字符。如果沒有返回DOM對象,也沒有錯誤產生,我們創(chuàng)建一個只包含告訴用戶二個文檔相同的一個<message>元素的文檔。

unless ( defined( $context->{domtree} )) {
my $msg = "Files are semantically identical.";
$context->{domtree} = $self->dom_from_data( { message => $msg } );
}
}

在完成信號收集事件后,我們就可以繼續(xù)編寫信號收集事件和SOAP調度程序共享的核心方法了。

首先,我們需要來創(chuàng)建compare()方法。它不僅僅是同名的XML::SemanticDiff的方法的容器,它還接受二個包含被比較的XML文檔的句柄并返回結果。

sub compare {
my $self = shift;
my ( $xmlstring1, $xmlstring2 ) = @_;
my $diff = XML::SemanticDiff->new( keeplinenums => 1 );
my @results = $diff->compare( $xmlstring1, $xmlstring2 );
return @results;
}

dom_from_data()方法通過XML::Generator::PerlData對任何公用Perl數據結構的引用進行處理創(chuàng)建一個XML::LibXML::Document對象(DOM樹形式的XML文檔),并將生成器與XML::LibXML::SAX::Builder連接生成DOM樹。還記得嗎,我們在結果事件回調中調用了該方法來創(chuàng)建包含有適當信息的DOM樹。

sub dom_from_data {
my ( $self, $ref ) = @_;
my $builder = XML::LibXML::SAX::Builder->new();
my $generator = XML::Generator::PerlData->new( Handler => $builder );
my $dom = $generator->parse( $ref );
return $dom;
}

最后,我們將創(chuàng)建compare_as_dom()方法。它也是最后的二個方法的容器,它以DOM樹的形式返回二個文檔的比較。

sub compare_as_dom {
my $self = shift;
my $diff_messages = $self->compare( @_ );
return undef unless scalar( @{$diff_messages} ) > 0;
return $self->dom_from_data( { difference => $diff_messages } );
}

1;

在創(chuàng)建了上面的方法后,我們就僅需要創(chuàng)建提供能夠供各種客戶端應用程序訪問的CGI腳本了,這也是需要綜合利用CGI::XMLApplication和SOAP::Lite 的地方。

#!/usr/bin/perl -w
use strict;
use SOAP::Transport::HTTP;
use WebSemDiff;

if ( defined( $ENV{'HTTP_SOAPACTION'} )) {
SOAP::Transport::HTTP::CGI
-> dispatch_to('WebSemDiff')
-> handle;
}
else {
my $app = WebSemDiff->new();
$app->run();
}

SOAP::Lite的dispatch_to()方法連接SOAP與一特定的模塊(或模塊的目錄)。在本例中,它使我們能夠重用實現瀏覽器界面的WebSemDiff類,模塊的共享意味著CGI只不過是一個請求代理,它提供了對基于連接客戶端應用應用程序類的方法的訪問。通過互聯(lián)網瀏覽器訪問應用程序的用戶被提示上傳二個XML文檔,并通過compare_as_dom()方法獲取結果,SOAP客戶端只可以直接訪問compare_as_dom、更低級的compare()等方法。

至此,我們已經開發(fā)了一個能夠運行的應用程序。下面我們就來用一些客戶端與它進行連接,比較二個文檔,并返回相應的結果。

為了簡明起見,我們將使被比較文檔盡量簡單。第一個文檔的名字為doc1.xml:

<?xml version="1.0"?>
<root>
<el1 el1attr="good"/>
<el2 el2attr="good">Some Text</el2>
<el3/>
</root>

第二個XML文檔的名字為:doc2.xml :

<?xml version="1.0"?>
<root>
<el1 el1attr="bad"/>
<el2 bogus="true"/>
<el4>Rogue</el4>
</root>

從瀏覽器進行訪問

對/cgi-bin/semdiff.cgi的請求將提示用戶上傳二個文檔:

 
圖1


在對文件進行比較后,結果如下:

 
圖2

從SOAP客戶端訪問

SOAP::Lite既有服務器也有客戶端實現。在這里我們將使用它創(chuàng)建一個連接我們的應用程序的SOAP界面的客戶端應用程序。為了節(jié)約篇幅,我們將跳過與變量處理、打開和讀取要比較的XML文檔相關的客戶端腳本,而重點討論與SOAP相關的部分:

#!/usr/bin/perl -w
use strict;
use SOAP::Lite;
...
my $soap = SOAP::Lite
-> uri('http://my.host.tld/WebSemDiff')
-> proxy('http://my.host.tld/cgi-bin/semdiff.cgi')
-> on_fault( &fatal_error );

my $result = $soap->compare( $file1, $file2 )->result;

print "Comparing $f1 and $f2...n";

if ( defined $result and scalar( @{$result} ) == 0 ) {
print "Files are semantically identicaln";
exit;
}

foreach my $diff ( @{$result} ) {
print $diff->{context} . ' ' .
$diff->{startline} . ' - ' .
$diff->{endline} . ' ' .
$diff->{message} .
"n";

}

將我們的二個XML文檔的路徑傳遞給該腳本代碼會產生下面的結果:

Comparing docs/doc1.xml and docs/doc2.xml...
/root[1]/el1[1] 3 - 3 Attribute 'el1attr' has different value in element 'el1'.
/root[1]/el2[1] 4 - 4 Character differences in element 'el2'.
/root[1]/el2[1] 4 - 4 Attribute 'el2attr' missing from element 'el2'.
/root[1]/el2[1] 4 - 4 Rogue attribute 'bogus' in element 'el2'.
/root[1] 5 - 5 Child element 'el3' missing from element '/root[1]'.
/root[1] 5 - 5 Rogue element 'el4' in element '/root[1]'.

另外,我們可以使用SOAP::Lite的自動調度機制來提高代碼的可讀性:

use SOAP::Lite +autodispatch =>
uri => 'http://my.host.tld/WebSemDiff',
proxy =>'http://my.host.tld/cgi-bin/semdiff.cgi',
on_fault => &fatal_error ;

my $result = SOAP->compare( $file1, $file2 );

print "Comparing $f1 and $f2...n";

# etc ..

從RESTful客戶端進行訪問

REST架構的愛好者會非常喜歡我們的應用程序能夠提供訪問未經轉換的XML文檔。

#!/usr/bin/perl -w
use strict;
use HTTP::Request::Common;
use LWP::UserAgent;

my ( $f1, $f2 ) = @ARGV;

usage() unless defined $f1 and -f $f1
and defined $f2 and -f $f2;


my $ua = LWP::UserAgent->new;
my $uri = "
http://my.host.tld/cgi-bin/semdiff.cgi";


my $req = HTTP::Request::Common::POST( $uri,
Content_Type => 'form-data',
Content => [
file1 => [ $f1 ],
file2 => [ $f2 ],
passthru => 1,
semdiff_result => 1,
]
);
my $result = $ua->request( $req );

if ( $result->is_success ) {
print $result->content;
}
else {
warn "Request Failure: " . $result->message . "n";
}

sub usage {
die "Usage:nperl $0 file1.xml file2.xml n";
}

該腳本(restful_semdiff.pl)能夠將下面的XML文檔輸出到STDOUT:

<?xml version="1.0" encoding="UTF-8"?>
<document>
<difference>
<context>/root[1]/el1[1]</context>
<message>
Attribute 'el1attr' has different
value in element 'el1'.
</message>
<startline>3</startline>
<endline>3</endline>
</difference>
<difference>
<context>/root[1]/el2[1]</context>
<message>
Character differences in element 'el2'.
</message>
<startline>4</startline>
<endline>4</endline>
</difference>
...
</document>

結論

在本文中我們完全沒有提到XML-RPC,原因有二個:

第一,SOAP::Lite提供的XML-RPC客戶端和服務器端界面與SOAP使用的非常相似,因此使用它意義不大。

第二,與SOAP客戶端不同的是,XML-RPC客戶端沒有與它們的請求相關聯(lián)的標準和明確的HTTP頭部,這意味著我們的CGI請求代理必須采取一定的措施來區(qū)分XML-RPC客戶端和正常的互聯(lián)網瀏覽器。通過對POST請求和“text/xml”的內容類型進行檢查,探測XML-RPC請求是可能的,但這種方案是“不健壯的”。

通過本篇文章的介紹,我衷心地希望讀者能夠掌握結合利用SOAP::Lite和CGI::XMLApplication創(chuàng)建簡潔、模塊化的支持通過SOAP、REST和HTML瀏覽器進行訪問的應用程序的方法。

發(fā)布:2007-03-25 10:34    編輯:泛普軟件 · xiaona    [打印此頁]    [關閉]
相關文章:
上海OA系統(tǒng)
聯(lián)系方式

成都公司:成都市成華區(qū)建設南路160號1層9號

重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務大廈18樓

咨詢:400-8352-114

加微信,免費獲取試用系統(tǒng)

QQ在線咨詢