日期:2012-09-28  浏览次数:20416 次

简单对象访问协议:SOAP的应用

SOAP,是由万维网联盟(W3C)制定的的一个新通讯协议:Simple Object Access Protocol(中文:简单对象访问协议)的英文缩写,目前已经得到IBM 、Ariba 、Commerce One 、SAP 、康柏、惠普等公司的支持。它能够让不同应用程序之间通过HTTP通讯协议,以 XML格式互相交换彼此的资料。由于HTTP通讯协议在网络上无所不在,而且XML解析程序又相当容易取得,所以SOAP能够很容易地被套用与开发。当然这些便利性是有代价的:牺牲了部份运行速度,因此SOAP本身并不是用来代替原有的低级程序,但是如果程序设计师的主要考虑在于能够很容易地与其它系统相互沟通,那么SOAP的确能够发挥它的功效。SOAP 开发工具在许多开发环境下已经可以取得了,包括 Python,Java,Visual Basic,Perl。本身具备远程过程调用 API 程序(例如 Java 的 RMI 或者微软的 COM+)开发经验的程序设计员将会发现SOAP开发工具使用起来有一种类曾似曾相识的感觉。

在这里我向大家介绍如何使用 Perl程序语言来开发网络服务(Web services),以及如何在SOAP服务器上面建立应用程序。

首先到http://www.soaplite.com 下载SOAP::Lite工具程序,它是一个Perl程序模块,只要安装好这套模块以及相关的函式库(相关资料地址:http://www.soaplite.com/#PREREQUISITES),大家便可以开始编写SOAP 服务程序了。
在开始设计一个SOAP倾听程序(listener)之前,我们得先了解一下SOAP如何处理来自客户端的请求。首先客户端会送出一个 XML文件给服务器,称为「SOAP 封装(envelope)」。服务器在接收到这个文件以后会分析这个文件,读取文件内含的类别名称(class name)与函数名称(function name),并且在这些名称与特定的Perl程序对象之间建立对应关系。在下面的范例程序中,我们建立一个称为World的类别,内含两个函数 HelloWorld 与 GoodbyeWorld:

package World;
sub new {
bless {}, shift;
};
sub HelloWorld {
my ($self) = @_;
return "Hello World\n";
};
sub GoodByeWorld {
my ($self,$adjective) = @_;
return "Goodbye $adjective World\n";
}
1;


虽然我们不太可能实际看见一个SOAP请求(request)长得是什么样子,但是了解这些SOAP请求内容的幕后工作方式,对于程序的侦错将会很有帮助。在这里,我们用下面这个非常简单的例子来说明SOAP客户端如何对前面提到的World类别提出请求,并且呼叫里面的 HelloWorld 函数:


<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENC=http://schemas.xmlsoap.org/soap/encoding/
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi=http://www.sorw.org/2001/XMLSchema-instance
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.sorw.org/2001/XMLSchema">
<SOAP-ENV:Body>
<namesp1:HelloWorld xmlns:namesp1="World"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


你可以在这个XML文件中很清楚地看见封包与主体(body)开始的地方:HelloWorld 函数是在 body 里面被呼叫的。在该行叙述中,命名空间(namespace)属性的值就是我们要呼叫的函数的名称,而XML命名空间(XML namespace)属性的值就是该函数所隶属的对象类别的名称。在这个例子里面,我们呼叫了World对象类别的 HellowWorld 函数。当我们要呼叫 GoodByeWorld 函数的时候,大部分内容都不需修改,只要将 body 的部分稍作修改即可:

<SOAP-ENV:Body>
<namesp2:GoodByeWorld xmlns:namesp2="World">
<c-gensym9 xsi:type="xsd:string">cruel</c-gensym9>
</namesp2:GoodByeWorld>
</SOAP-ENV:Body>


由于SOAP本身是通过HTTP通讯协议来传递,因此我们可以直接利用Perl本身提供的Web服务器相关功能。首先编写一个简单的CGI程序,这个程序会把接收到的请求传递给我们之前写好的World类别,并且把SOAP产生的响应信息回传给浏览器。在开始执行之前请别忘记确认你的服务器支持CGI程序,并且要把该程序的权限设定为允许执行。下面这个CGI程序会接收SOAP请求,并且传递给我们之前写好的World类别:

#!/usr/bin/perl
use SOAP::Transport::HTTP;
use World;
SOAP::Transport::HTTP::CGI
dispatch_to('World')
-> handle;


我们要做的就只有这些,剩下的部分全都交给SOAP模块来处理就行了。你必须为每个你打算呼叫的SOAP类别都分别写一个类似上面的CGI程序。此外要注意的是,在上面的 dispatch_to() 方法里面所用到的参数就是你想要连结的SOAP类别的名称。在编写SOAP类别的时候要注意这些类别必须和其它类别一样,一定要有一个名为 new 的方法(method)$self来做为呼叫其它方法时所使用的第一个参数。把包装名称(package name)放在程序代码的第一行,然后把这个模块以 .pm 做为扩展名储存起来(请参考前面提到的World类别范例程序)。

SOAP::Lite 也可以根据所要求的名称来动态加载不同的模块,这项功能可以让我们编写一个能够加载某个目录中任何一个模块的CGI程序。这种做法比较缺乏安全性,而且也比较不容易控制究竟要加载哪个模块,不过把各个SOAP模块统一放置在同一个目录下,在档案管理上的确会比较方便。要动态加载不同的SOAP模块,我们必须把相关的 use 叙述拿掉,并且把存放SOAP模块档案的目录路径当成dispatch_to() 方法的第一个参数:

#!/usr/bin/perl
use SOAP::Transport::HTTP;
SOAP::Transport::HTTP::CGI
dispatch_to('/home/httpd/soap_modules/')
-> handle;


SOAP客户端程序就像SOAP服务器程序一样都很容易编写。你只需要加载 SOAP::Lite 模块,并且知道一些远程服务(或者说「端点 endpoint」)的相关信息就行了。事实上,收集远程服务的相关信息可能正是最困难的部分。你必须知道远程服务器的URL,服务器所提供服务内含方法的命名空间URI,以及这些方法的名称与需要传入的参数。一旦取得了这些信息,我们就可以开始编写一个SOAP客户端程序了。在下面的范例程序里面,我为前面刚建立好的SOAP服务器写一个客户端程序:

use SOAP::Lite;
my $s = SOAP::Lite
->uri('World')
->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
->HelloWorld();
print $s->result();
my $s = SOAP::Lite
->uri('World')
->proxy('http://soapserver.mycompany.com/soap/soapserver.cgi')
->