深蓝海域KMPRO

乐趣无穷,可能无限的新技术-web service

2002-09-16 10:33

乐趣无穷,可能无限的新技术-web service

虽然电子商务的狂热在最近似乎有减温的现象,让许多人能够回归到正常的步调之中,不过随着电子商务而发展的软件技术并没有稍停脚步,反而更加蓬勃发展。因为由这些技术创造的应用早已成为许多人生活的一部份,甚至是开启未来趋势的基石。在目前最热门且最被看好的技术便是所谓的web service了,那么什么是web service呢?

简单的说,web service是一种想把全世界的internet/intranet变成一个虚拟计算环境的观念和技术。在由web service组成的虚拟环境中使用者可以任何的客户端软件,例如浏览器,一般的window或是java应用程序或是电子行动设备等,来呼叫web service提供的服务。而web service本身则可以由任何的技术实作,例如开发者可以使用delphi,java,c/c++或是c#等的语言和工具来开发。

web service是建立在开放和标准的规格之上,允许异质的客户端呼叫以使用它提供的服务。因此各种异质的客户端必须使用一种共通的沟通标准才能够顺利的和由各种不同技术实作的web service互通。目前最流行而且最具潜力的沟通标准当属soap了。

soap (simple object access protocol)是由don box起草,并且获得ibm,microsoft,lotus和userland等大型公司支持而成为w3c标准之一的通讯协议规格。从soap的名称中我们便可以知道它是让客户端呼叫远程对象服务的一种机制。soap以xml标准封装呼叫远程服务的格式,有别于其它分布式对象模型呼叫特定的呼叫格式,例如corba的giop以及dcom的orpc。由于soap以xml封装呼叫格式,因此它可以使用任何的实体传输层来传送,例如http,tcp或是smtp等。也许让我们使用一个简单的概例来说明会让各位更容易的了解。

假设现在我在linux平台上以java语言实作了一个web service,这个web service提供了一个服务getsystemtime。这个服务接受一个使用者名称和一个密码,如果成功的登录之后,这个服务便会回传linux平台目前的系统时间。那么我可以使用delphi以soap的标准封装使用者名称和密码来呼叫这个在linux平台上的getsystemtime服务。例如下面就可能是由soap封装的格式:

gordonli

xx12yh_49

藉由soap,delphi的客户端应用程序可以轻易的呼叫linux平台上的web service,而无需关心这个web service是由什么技术实作的,或是存在于任何地方,更不需要以特定的二进制格式来封装呼叫。因此藉由web service和soap,开发者可以轻易的整合各种异质平台,异质分布式对象模型,而充分的利用所有的计算资源,这在以前是不可能轻易做到的,同时web service和soap也为未来的发展开启了另一扇的大门。目前web service已经在国外快速的蓬勃发展,各种web service也已经在internet上供人使用,例如搜寻mp3的服务,或是查询全世界各地气象的服务等。相信web service和soap也将很快的在国内发展起来,也终将成为软件开发人员必备的软件技能之一。

web service本身包含了许多的意义,观念和技术,在run!pc 2001年5月份的『解析web service的技术内容与意涵』一文中已经对于web service和soap有基本的介绍,读者可以参考该文的说明。

本篇文章的内容在于讨论web service的技术架构和实作的技巧,并且首先以delphi 6做为说明如何实际的开发web service以及客户端应用程序来呼叫web service。接着再说明如何使用delphi开发的客户端应用程序来呼叫internet上由java开发的web service,来向各位读者展示web service和soap的开放性以及标准性。当我们成功的在本地机器呼叫了在世界上某一个角落,由某一个人使用某一种工具开发的web service时,相信读者也会赞叹web service和soap所带来的无限可能和下一波的软件技术的革命。

web service和soap的架构

那么我们要如何才能够知道每一个web service提供的服务?要如何才能够呼叫到web service?又要到那里找到适合的web service呢?简单的说,web service提供的服务是以所谓的wsdl(web service description language)标准来叙述的,只要我们能够取得特定web service的wsdl,就可以从其中了解它提供的服务,以及如何呼叫这个web service。

最后一个问题是如何找到适用的web service,在目前全世界已经有人公布了许多的web service供人呼叫使用。此外ibm和microsoft等公司也正在研拟所谓的uddi标准以提供注册,搜寻,交换和使用web service的标准,开发人员可以藉由uddi找到需要的web service,当然我相信许多的web service将会由开发人员根据自己的需求而使用工具开发出来。

说了那么多,可能读者会想要知道到底web service要如何实作?要使用什么语言或是工具才能够撰写的呢?事实上web service并不限定任何特定的工具或是语言才能够开发,简单的说你可以使用任何的工具或是语言来开发,甚至可以使用asp/jsp等稿本语言(script language)来实作。当然,开发人员也可以结合各种不同的软件技术和组件架构来开发。

下图是以比较实体架构的观点来叙述web service的观念。图中的客户端藉由soap和http通讯协议,透过web service provider找到适合的web service,再呼叫它。而实体的web service可以是实作在window平台的mts/com+或是.net对象,也可以是实作在linux\unix平台中的corba或是ejb对象。这个观点是以各种组件模型来实作web service。

 

图一 web service的架构示意图

至于下图则是以更细微的观点来看web service的实体架构。在这个图形中呈现了web service可以由asp/jsp或是cgi,isapi的形式来实作,以服务客户端的请求。开发人员可以把所有的web service企业逻辑实作在asp/jsp/cgi/isapi/dso之中。或是只把响应客户端请求的逻辑实作在asp/jsp/cgi/isapi/dso之中,而把真正的企业逻辑实作在后端的组件模型之中,或是后端的应用程序之中,例如delphi的datasnap服务器。

 

图二 web service的实作架构图

从上面的讨论中可以知道,开发人员可以使用任何的技术实作web service,只需要根据标准输出web service,就可以由客户端呼叫使用之。
讨论完了观念之后,现在让我们回到实际的实作层次中。虽然web service可以由任何的技术实作,但是开发人员仍然需要选择一种方式来开发。开发web service除了实作web service的企业逻辑之外,也必须提供web service的wsdl,并且分发web service。由于目前使用web service的情形仍然以soap/http型式呼叫,因此许多的web service也是以web应用程序的型态实作,例如把web service实作成cgi或是isapi/dso的型式,不过只要能够处理http的呼叫,web service也可以实作成一般的独立应用程序,这一点是读者必须了解的。

开发web service虽然不是非常的困难,但是它仍然需要许多的开发步骤和处理程序,这也仍然需要花费一些开发成本。在borland最新推出的delphi 6中,borland特别提供了7个直接的web service组件,三个web service精灵以及其它数个相关的vcl组件来帮助开发人员快速的开发web service,更棒的是,开发人员也可以再结合delphi原有的mts/com+/corba/ejb组件模型开发更具延展性的web service。下图便是delphi 6中直接和web service相关的web service组件组。

 

图三 delphi 6提供的webservices组件

这7个web service组件可以让开发人员呼叫远程web service,自动产生web service的wsdl,以及进行soap/http和object pascal语言之间的系结(binding),以便让delphi的程序员能够使用object pascal直接处理soap之中的讯息。

下图则显示了在客户端应用程序和远程web service之间如何藉由这些组件沟通,以及每一个组件之间的关系。由于delphi 6是window平台上的开发工具,因此它使用了wininet.dll来传送http封包信息。

 

图四 delphi 6 webservices组件的功能示意图

从上图可以知道,delphi 6客户端应用程序藉由thttprio呼叫远程web service,而toptosoapdomconvert可以把object pascal的呼叫和参数自动转换为soap封装的格式信息,再藉由thttpreqresp传送http封包。而在伺服端thttpsoapdispatcher则负责处理客户端传送来的soap/http信息,并且透过thttpsoappascalinvoker组件来自动启动能够处理这个soap/http请求的object pascal程序代码。至于twsdlhtmlpublish则能够自动的根据delphi实作的web service来产生wsdl并且输出此wsdl让客户端应用程序能够使用这个wsdl来呼叫web service。

说明了delphi 6中有关web service的组件和其功能之后,现在就让我们看看在delphi 6中开发web service的步骤。下图便是在delphi 6中开发web service简易的步骤:

 

图五 使用delphi 6开发 web service的步骤

首先程序员必须撰写web service的核心逻辑,然后定义此web service的wsdl,以便让客户端能够遵循标准呼叫。在实作完web service之后,接着程序员就可以使用delphi 6提供的webservices组件组来实作客户端应用程序,并且藉由wsdl来呼叫web service。

在实作web service客户端应用程序时,delphi 6提供了非常弹性的方法,允许程序员使用early binding或是late binding,不像某些解决方案只允许使用late binding。这种设计可以让程序员在开发web service解决方案时可以根据执行效率或是执行弹性来决定使用early binding或是late binding。
为了让读者能够真正的了解如何开发web service系统并且范例delphi 6在soap和web service方面的强劲功能,就让我们使用一个实际的范例来说明如何使用delphi 6快速开发web service和客户端应用程序,并且结合数据库来提供客户端信息。这个范例web service是把myessays数据表中的所有我写的文章输出给客户端以便查询信息,而且不管客户端是浏览器,一般的window应用程序,或是linux下的应用程序都可以。下图便是储存这些文章信息的画面:

 

图六 范例数据表

至于这个范例的整体架构如下图所示。文章信息是储存在interbase之中,并且藉由delphi 6的dbexpress来技术存取。至于实作web service的主体则是一个由delphi 6撰写的简单web应用程序。最后我们实作一个原生窗口应用程序藉由soap来呼叫此web应用程序实作的web service。

 

图七 实作web service的架构图

步骤1 – 开发soap伺服端应用程序

首先程序员可以在delphi中使用soap server application精灵来开发web service服务器。在delphi 6中我们只需要点选file|new|others菜单,然后点选webservices页次即可看到下图的画面,然后再点选soap server application图像。

 

图八 delphi 6的soap server application精灵

在点选了soap server application图像之后,delphi会询问程序员要以那一种的实体型态来实作web service,如下图所示。程序员可以选择欲实作的程序型态,例如在这个范例中我选择以web app debugger executable来实作,因为这个程序型态可以让我们在开发web service时能够轻易的除错。当然,程序员也可以选择以一般的window应用程序来实作web service,而不使用soap server application精灵提供的下列实体型态的程序。

 

图九 以delphi 6的web app debugger应用程序的形式开发web service

在点选了图九的程序型态和ok按钮之后,delphi便会自动帮助程序员产生如下的web模块。

 

图十 delphi 6建立建立的web module以及webservices组件

在图十的web模块之中,delphi自动产生的thttpsoapdispatcher组件可以让web server自动呼叫此应用程序,而twsdlhtmlpublish组件则可以自动产生叙述此web service的wsdl内容。


 

图十一 范例web service的主窗体

现在再让我们在这个web service程序的主窗体中加入一个tlabel并且设定它的caption特性值为『我的第一个web service』,如上图所示。

步骤 2 – 定义web service的服务接口并且实作它

接下来的步骤便是真正的实作此web service。首先在delphi 6中建立数据模块,并且使用dbexpress连结到interbase:

 

图十二 范例web service的数据模块,它使用dbexpress组件存取interbase

点选file|new|unit菜单定义如下的imyessays接口,这个接口定义了此web service提供的服务,客户端应用程序可以呼叫getessaytitles函式取得所有文章的信息,而这些信息是储存在tessaysinfos的数组中,这个方法展示了delphi 6的web service可以处理复杂的数据型态。至于getessaycontent则可以根据客户端传递来的文章id而回传此文章的内容给客户端应用程序。

unit umyessaysinf;
interface
uses
types, xsbuiltins, uessaysinfo;
type
imyessays = interface(iinvokable)
['{1c8aba87-455b-4430-9881-239f5ffe7f49}']
function getessaytitles : tessaysinfos ; stdcall;
function getessaycontent(const iid : integer) : string; stdcall;
end;
implementation
uses
invokeregistry;
initialization
invregistry.registerinterface(typeinfo(imyessays));
end.

接着我们定义储存文章信息的数据结构。为了让文章信息能够自动传递回客户端,我们可以在delphi 6中从tremotable类别继承子类别,如此一来delphi 6便会自动帮助我们处理数据marshalling的问题。最后必须呼叫delphi 6提供的remclassregistry对象来注册这些类别。

unit uessaysinfo;
interface
uses
invokeregistry, xsbuiltins;
type
tessaysinfo = class(tremotable)
private
fessayid : integer;
fessaytitle : widestring;
published
property essaytitle : widestring read fessaytitle write fessaytitle;
property essayid : integer read fessayid write fessayid;
end;

tessaysinfos = array of tessaysinfo;
implementation

initialization
remclassregistry.registerxsclass(tessaysinfo, 'http://www.w3.org/2001/xmlschema', 'tessaysinfo', '',false );
remtyperegistry.registerxsinfo(typeinfo(tessaysinfos), 'http://www.w3.org/2001/xmlschema', 'tessaysinfos');
finalization
remclassregistry.unregisterxsclass(tessaysinfo);
remtyperegistry.unregisterxsinfo(typeinfo(tessaysinfos));
end.

现在到了实作imyessays接口的时候了,我们定义tmyessays类别从tinvokableclass继承下来并且实作imyessays接口。tinvokableclass类别可以让客户端从远程呼叫。最后同样呼叫invregistry对象注册tmyessays类别,以便让thttpsoapdispatcher组件可以启动。至于imyessays接口之中的getessaytitles方法则是使用dbexpress从interbase中读取所有的数据,并且把文章的id和名称储存在一个tessaysinfo对象中,再把tessaysinfo对象储存在tessaysinfos数组中,最后回传此数组给客户端。

unit umyessaysimpl;
interface
uses
sysutils, classes, invokeregistry, xsbuiltins, umyessaysinf, uessaysinfo, db, httpprod, udmmyessays, dbxpress;
type
tmyessays = class(tinvokableclass, imyessays)
private
procedure createdatamodule;
procedure freedatamodule;
public
{ iisapitutorials }
function getessaytitles: tessaysinfos; stdcall;
function getessaycontent(const iid : integer) : string; stdcall;
end;
implementation
{ tisapitutorials }

procedure tmyessays.createdatamodule;
begin
dmmyessays := tdmmyessays.create(nil);
end;

procedure tmyessays.freedatamodule;
begin
if (assigned(dmmyessays)) then
begin
dmmyessays.free;
dmmyessays := nil;
end;
end;

function tmyessays.getessaycontent(const iid: integer): string;
begin
result := '尚未实作, 请待续!!!';
end;

function tmyessays.getessaytitles: tessaysinfos;
var
ino : integer;
iid : integer;
einfo : tessaysinfo;
td: ttransactiondesc;
begin
createdatamodule;

td.transactionid := 1;
td.isolationlevel := xilreadcommitted;
try
dmmyessays.sconnmyessays.starttransaction(td);
ino := dmmyessays.sdsmyessays.recordcount;
setlength(result, ino);
iid := -1;
with dmmyessays.sdsmyessays do
begin
while not eof do
begin
inc(iid);
einfo := tessaysinfo.create;
einfo.essayid := fieldbyname('eid').value;
einfo.essaytitle := fieldbyname('etitle').value;
result[iid] := einfo;
next;
end;
end;
finally
dmmyessays.sconnmyessays.commit(td);
freedatamodule;
end;
end;
initialization
invregistry.registerinvokableclass(tmyessays);
end.

现在这个能够处理复杂资料的web service便藉由delphi 6提供的webservices组件和精灵完成了,接下来就是开发客户端应用程序来呼叫此web service以取得文章信息了。

步骤 3 – 开发客户端应用程序呼叫web service

使用delphi 6开发呼叫web service的客户端应用程序更简单,因为delphi 6提供的webservices组件组中的thttprio组件实在是太方便了,我们只要使用对象检视器设定thttprio组件的wsdllocation特性值为欲呼叫的web service的wsdl,那么thttprio组件便可以自动的处理所有呼叫web service的细节。

例如下面便是使用delphi 6开发的客户端应用程序,在这个应用程序的主窗体上使用了一个thttprio组件,并且在它的wsdllocation特性值中输入刚才开发的web service的wsdl档案的地址。

 

图十三 范例客户端应用程序的主窗体

接着在『 我的文章』按钮的onclick事件处理函式中撰写如下的程序代码:

procedure tform2.bitbtn1click(sender: tobject);
var
oricursor : tcursor;
einfos : tessaysinfos;
icount : integer;
lstart, lend : longint;
begin
showcaption;

statusbar1.panels[0].text := '呼叫web service中...';
statusbar1.refresh;
lvmyessays.items.beginupdate;
lvmyessays.items.clear;
oricursor := screen.cursor;
screen.cursor := crhourglass;
lstart := gettickcount;
try
einfos := (httprio1 as imyessays).getessaytitles;
for icount := low(einfos) to high(einfos) do
begin
with lvmyessays.items.add do
begin
caption := einfos[icount].essaytitle;
data := pointer(einfos[icount].essayid);
end;
end;
finally
lend := gettickcount;
showruntime(lstart, lend);
lvmyessays.items.endupdate;
statusbar1.panels[0].text := '完成呼叫web service';
statusbar1.refresh;
screen.cursor := oricursor;
end;
end;

procedure tform2.showcaption;
begin
lblcaption.caption := '太棒了, 我的第一个web service程序';
end;

procedure tform2.showruntime(const lstart, lend: integer);
begin
statusbar1.panels[1].text := floattostr((lend - lstart) / 1000.0) + '秒';
end;

上面的程序代码藉由thttprio组件呼叫imyessays接口的getessaytitles方法,取得tessaysinfos数组,再从数组中一一的取出每一篇文章的名称,最后再填入到主窗体中的tlistview组件之中。下图就是执行此客户端应用程序呼叫web service服务器,并且取得所有文章信息的画面。从这么简单的数个步骤中,我们已经使用delphi 6开发了一个真正的web service应用系统。

 

图十四 范例客户端应用程序呼叫web service得到数据的画面

虽然这是我们使用delphi 6建立的第一个web service,但是这个范例web service展示了delphi 6的soap/web service解决方案能够轻易的传递复杂的数据型态,因为在范例web service中是使用数组的型态来传递所有的文章信息。delphi 6的soap/web service技术绝不是像一些工具只提供简单的soap/web service解决方案,而是充分的提供了一般和复杂的应用程序,并且能够整合各种组件模型,是目前最具威力,也是最先进的soap/web service开发工具之一。

delphi 6除了提供强劲的web service开发功能之外,新的web app debugger不但可以帮助程序员除错web应用程序之外,也可以帮助程序员监督客户端应用程序和web service之中传递的信息。这些信息包含了soap的封包,以及web service服务器回传回客户端的所有soap封包。这对于程序员学习soap和解析soap payload都非常有帮助。例如下图便是我使用web app debugger检视此范例web service应用系统传递的soap payload。 

图十五 delphi 6的web app debugger可以显示客户端和伺服端之间所有的讯息


为了证明web service的威力和相通性,目前全世界已经有许多人成立了各种web service的网站,让开发人员能够测试web service。例如现在www.xmethods.com便是提供各种web service的网站之一。这个网站罗列了许多的web service,下图便是这个网站目前提供的web service。

 

图十六 internet上的xmethods(www.xmethods.com)提供了许多的web service供人呼叫使用

现在让我们使用delphi 6开发一个客户端应用程序来透过internet/intranet呼叫远程由java实作,执行在apache上的一个查询美国各州气温的web service。下图是这个web service的详细信息,这个web service的作者甚至提供了java客户端应用程序展示如何呼叫这个气温web service,不过现在我想使用delphi的客户端应用程序来呼叫,而不是java。


 
图十七 xmethods上的众多web service之一,temperature web service

下图便是在我的机器中使用delphi 6开发的客户端应用程序,藉由delphi 6的webservices组件组来呼叫这个位于远程,我也不知道在什么地方的气温web service的结果画面。

从下图中可以证明,虽然我并不知道这个web service在那里,我仍然可以藉由web service的标准接口叙述wsdl来使用它,即使它是使用java实作的,并且执行在apache之上。


 
图十八 delphi实作的客户端应用程序呼叫执行在远程apache上的web service

希望上面的内容可以让各位读者了解web service和soap在应用上的潜力以及delphi 6提供的组件技术可以让开发人员快速而且轻易的实作出各种威力强大的web service。

也许藉由web service和soap的出现,也会对于目前应用系统架构产生巨大的影响。例如现在『供应链』软件非常的流行,但是许多的供应链软件在整合上,中,下游厂商时,经常会需要所有的厂商使用相同的平台以及基层软件。但是对于下游厂商而言,可能无法像上游厂商一样使用昂贵的设备,例如unix box和大型erp软件,许多的下游小厂也许只能使用window nt或是linux平台。不过现在web service和soap可以提供非常完善的解决方案,就如同下图显示的一样,下游厂商可以只使用asp提供简单的web service让他的中游厂商呼叫。而上游厂商则可以在unix box中藉由大型的erp软件呼叫中游厂商执行在window 2000中的biztalk service。如此一来不但每一个厂商都可以选择最适合的执行平台和软件,也可以藉由web service和soap整合上,中,下游厂商而提供一个及时且完备的供应链。web service和soap正为软件带来无限的发展契机。

 

图十九 web service的应用架构之一

虽然soap和web service目前已经成为标准并且也已经被世界厂商所接受和支持。但是soap和web service仍然是在成长期,功能规格仍然在继续的改善和强化之中,因此soap和web service的变化也在预期之中。delphi 6实作的soap和web service似乎是比较偏向ibm和java的阵营,因此delphi 6能够很容易的和java以及php,perl等soap/web service解决方案整合。至于microsoft的soap和web service则稍有不同,需要程序员特别注意一下。
如果下次有时间的话,那么就让我们继续讨论microsoft的soap toolkit以及.net之中的soap解决方案,并且比较delphi和它们之间的差异以及如何整合这些不同的soap实作技术。

相关推荐