當前位置:工程項目OA系統(tǒng) > 泛普各地 > 上海OA系統(tǒng) > 上海OA快博
用Perl和XML輕松開發(fā)多種界面的Web服務
用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文檔的內(nèi)容。
閱讀本文之前,我建議讀者應當對CGI::XMLApplication有個基本的了解。對它有一定的了解會對理解我們本篇文章的內(nèi)容有所幫助。事實上只要了解一個典型的包括三個部分的CGI::XMLApplication應用程序就夠了:連接客戶端和應用程序的CGI腳本、處理任務的Perl模塊以及將Perl模塊中返回的DOM樹轉(zhuǎn)換為客戶端應用程序能夠接受的XSLT樣式表。
理解CGI::XMLApplication的基本架構(gòu)是十分重要的,因為SOAP::Lite模塊也使用了相同的架構(gòu),開發(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的繼承關系后,我們需要實現(xiàn)使瀏覽器界面工作的方法。
瀏覽器界面有二種狀態(tài):缺省狀態(tài)是提醒用戶上傳二個XML文檔進行比較,顯示比較結(jié)果的結(jié)果狀態(tài)(或在比較時出現(xiàn)的錯誤)。selectStylesheet()方法返回由應用程序生成的DOM樹轉(zhuǎn)換成的樣式表的路徑。在這里我們不對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對象。在向瀏覽器返回結(jié)果前,由selectStylesheet()方法設定的XSLT樣式表將對該文檔對象進行轉(zhuǎn)換。
sub getDOM {
my ( $self, $context ) = @_;
return
$context->{domtree};
}
getXSLParameter()方法提供了從類向樣式表傳送值的一種方式(可以通過<xsl:param>元素獲得該值)。在這里,我們只增加所有的請求參數(shù),讓樣式表來選擇相關的域。
sub getXSLParameter {
my $self = shift;
return
$self->Vars;
}
由于缺省狀態(tài)只是一個不要求應用程序邏輯或特別處理的簡單提示,因此我們只需實現(xiàn)對結(jié)果狀態(tài)的訪問即可:
# 登錄事件和回調(diào)事件
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文檔的文件句柄。我們首先檢查二個句柄是否存在,如果存在,則轉(zhuǎn)換為二個簡單的標量:
if ( defined( $fh1 ) and defined( $fh3 ) ) {
local $/ =
undef;
$file1 = <$fh1>
$file2 = <$fh3>;
其次,我們創(chuàng)建包含由通過調(diào)用compare_as_dom()方法生成的比較結(jié)果的DOM樹。將這次調(diào)用封裝在一個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對象,也沒有錯誤產(chǎn)生,我們創(chuàng)建一個只包含告訴用戶二個文檔相同的一個<message>元素的文檔。
unless ( defined( $context->{domtree} )) {
my $msg = "Files
are semantically identical.";
$context->{domtree} = $self->dom_from_data( {
message => $msg } );
}
}
在完成信號收集事件后,我們就可以繼續(xù)編寫信號收集事件和SOAP調(diào)度程序共享的核心方法了。
首先,我們需要來創(chuàng)建compare()方法。它不僅僅是同名的XML::SemanticDiff的方法的容器,它還接受二個包含被比較的XML文檔的句柄并返回結(jié)果。
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數(shù)據(jù)結(jié)構(gòu)的引用進行處理創(chuàng)建一個XML::LibXML::Document對象(DOM樹形式的XML文檔),并將生成器與XML::LibXML::SAX::Builder連接生成DOM樹。還記得嗎,我們在結(jié)果事件回調(diào)中調(diào)用了該方法來創(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與一特定的模塊(或模塊的目錄)。在本例中,它使我們能夠重用實現(xiàn)瀏覽器界面的WebSemDiff類,模塊的共享意味著CGI只不過是一個請求代理,它提供了對基于連接客戶端應用應用程序類的方法的訪問。通過互聯(lián)網(wǎng)瀏覽器訪問應用程序的用戶被提示上傳二個XML文檔,并通過compare_as_dom()方法獲取結(jié)果,SOAP客戶端只可以直接訪問compare_as_dom、更低級的compare()等方法。
至此,我們已經(jīng)開發(fā)了一個能夠運行的應用程序。下面我們就來用一些客戶端與它進行連接,比較二個文檔,并返回相應的結(jié)果。
為了簡明起見,我們將使被比較文檔盡量簡單。第一個文檔的名字為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
在對文件進行比較后,結(jié)果如下:
圖2
從SOAP客戶端訪問
SOAP::Lite既有服務器也有客戶端實現(xiàn)。在這里我們將使用它創(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文檔的路徑傳遞給該腳本代碼會產(chǎn)生下面的結(jié)果:
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的自動調(diào)度機制來提高代碼的可讀性:
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架構(gòu)的愛好者會非常喜歡我們的應用程序能夠提供訪問未經(jīng)轉(zhuǎn)換的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)能夠?qū)⑾旅娴腦ML文檔輸出到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>
結(jié)論
在本文中我們完全沒有提到XML-RPC,原因有二個:
第一,SOAP::Lite提供的XML-RPC客戶端和服務器端界面與SOAP使用的非常相似,因此使用它意義不大。
第二,與SOAP客戶端不同的是,XML-RPC客戶端沒有與它們的請求相關聯(lián)的標準和明確的HTTP頭部,這意味著我們的CGI請求代理必須采取一定的措施來區(qū)分XML-RPC客戶端和正常的互聯(lián)網(wǎng)瀏覽器。通過對POST請求和“text/xml”的內(nèi)容類型進行檢查,探測XML-RPC請求是可能的,但這種方案是“不健壯的”。
通過本篇文章的介紹,我衷心地希望讀者能夠掌握結(jié)合利用SOAP::Lite和CGI::XMLApplication創(chuàng)建簡潔、模塊化的支持通過SOAP、REST和HTML瀏覽器進行訪問的應用程序的方法。
- 1為你的網(wǎng)絡服務制作文檔
- 2Amazon和Google開辟Web service新紀元
- 3用戶認證和數(shù)字證書為Web服務保安全
- 4知識型企業(yè)的上海OA策略(By AMT 宋亮)
- 5Web Services和J2EE:集成中的伙伴
- 6Web技術、上海OA與高等教育(by AMT 劉宇 編譯)
- 7OA辦公軟件系統(tǒng)中任務管理是企業(yè)老板最關心的功能
- 8上海OA戰(zhàn)略
- 9數(shù)字資產(chǎn)管理:捕獲競爭優(yōu)勢的新方式(by AMT 劉宇 編譯)
- 10經(jīng)驗知識的獲取和作用
- 11上海OA--衰退期成長的動力
- 12WEB服務“不是”什么
- 13Web服務與二進制傳輸
- 14將經(jīng)驗和信息轉(zhuǎn)化為生產(chǎn)力
- 15架起結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)之間的橋梁(AMT 唐曉輝 編譯)
- 16美公司推出下一代網(wǎng)絡服務工具包
- 17用Perl和XML輕松開發(fā)多種界面的Web服務
- 18論文:信息系統(tǒng)開發(fā)過程中的上海OA(By AMT 宋亮)
- 19證據(jù)顯示微軟曾阻礙Sun參加網(wǎng)絡服務標準組織
- 20Web服務之路越走越亮
- 21e信 知識生產(chǎn)新生態(tài)
- 22論文:知識型企業(yè)的上海OA策略(By AMT 宋亮)
- 23上海OA日趨重要
- 24聯(lián)合國《2003 年電子商務與發(fā)展報告》
- 25泛普(上海)OA績效有以下特點
- 26上海OA和信息技術有什么關系?(by AMT 仲英豪編譯)
- 27Sun四處游說 要加入WS-I組織董事會
- 28OA辦公軟件系統(tǒng)中的工作“民意調(diào)查”很強大
- 29麥肯錫電子商務論叢-讓老百姓輕松上網(wǎng)洽公,免再苦苦排隊等候
- 30[經(jīng)典] 如何做好上海OA實施這道大餐?(AMT 孔祥云、徐家俊、孟凡強)
成都公司:成都市成華區(qū)建設南路160號1層9號
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務大廈18樓