分享
 
 
 

DELPHI 6 抢先研究-- BizSnap/SOAP/WebService 之二

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

DELPHI 6 抢先研究-- BizSnap/SOAP/WebService 之二

-- 通过 SOAP 传递自定义类型数据

在前一个例子(见 《DELPHI 6 抢先研究 -- BizSnap/SOAP/WebService 之一 -- 一个 Hello world! 的例子》)中我们看到,通过 SOAP 可以很方便地进行远程对象调用,虽然那个例子用的对象是一个 Delphi 类,但实际上只需要对对象作一个 SOAP 包装,即可调用包括 COM/CORBA/EJB 等各种对象(除 EJB 必须用 Java 实现外, COM/CORBA 都已可以用 Delphi 实现)。在那个例子中,接口方法用到的数据类型都是标准类型,但实际应用中常常会碰到要传递自定义类型的情况,这时的操作略麻烦一些,详情如李维 《樂趣無窮,可能無限的新技術-Web Service》 一文中的例子所示。

同样,这里也要用一个例子来说明通过 SOAP 传递自定义数据类型的方法,这个例子会是一个比较麻烦的例子:

服务端:

1.New|WebServices|Soap Server Application ,如下图:

这个例子是用 Web App Debugger (详见《DELPHI 6 抢先研究 -- Web 应用开发及调试》), 设置其 CoClass Name 为 wadSoapDemo2 , 如下图:

2.SaveAll , Unit2 命名为: SvrWMMain , Unit1 不改名, Project1 命名为: Server ;

3.New|Data Module ,将此单元保存为 SvrDataMod ;

4.在其中放入两个 dbExpress 控件: SQLConnection1 和 SQLDataSet1 ,如下图:

其属性设置为:

SQLConnection1

ConnectionName := IBLocal;

LoginPrompt := false;

Params.Values['Database'] := '[...]\Examples\Database\Employee.gdb';

// 上面的 [...] 为你的 InterBase 安装路径

SQLDataSet1

SQLConnection := SQLConnection1;

CommandText := 'select FULL_NAME, PHONE_EXT from EMPLOYEE WHERE EMP_NO = :EMP_NO';

5.New|Unit ,将此单元保存为 SvrDataType ,其内容如下:

unit SvrDataType;

interface

Uses

InvokeRegistry;

Type

TEmpInfo = Class( TRemotable )

Private

FName : String;

FPhone : String;

published

Property Name : String Read FName Write FName;

Property Phone : String Read FPhone Write FPhone;

end;

implementation

Initialization

RemClassRegistry.RegisterXSClass( TEmpInfo );

Finalization

RemClassRegistry.UnRegisterXSClass( TEmpInfo );

end.

此单元中定义了类: TEmpInfo ,用于记录员工信息,包括 Name 和 Phone 两个域,均为字符串类型。对于需要传递到客户端的数据类型,必须从 TRemotable 类派生,它能够自动处理类型信息的传递。如果要手工处理自定义数据类型的传递,则必须从 TRemotableXS 类派生,其用法与 TRemotable 类似,但这样的话,必须实现两个转换方法: NativeToXS 和 XSToNative ,详见 Delphi6\Source\Soap\XSBuiltIns.pas 中的几个类的实现。

需要注意的是,此类中将两个属性放在 Published 中,这里一定要这么做,我曾经因为将它们放在了 Public 中,导致客户端无法取得服务端的数据类型信息,后来才发现它们必须放在 Published 中才行,所以虽然这里并不是控件,这些属性也不是为了要在 Object Inspector 中显示,但仍然需要放在 Published 中。这可能是因为 Published 较 Public 多一些 RTTI(Run Time Type Info,运行时类型信息) 的东东,而远程数据类型是依赖于 RTTI 的。

最后是在远程类注册信息库中注册和反注册此类。

6.New|Unit ,将此单元保存为 SvrSoapIntf ,其内容如下:

unit SvrSoapIntf;

interface

Uses

InvokeRegistry, SvrDataType;

Type

ISoapEmployee = Interface( IInvokable )

['{31903B5A-96B3-43C2-A7B5-F67F6DB829E5}']

Function GetEmployee( aEmpNo : Integer ) : TEmpInfo; StdCall;

End;

implementation

Initialization

InvRegistry.RegisterInterface( TypeInfo( ISoapEmployee ) );

end.

此单元中定义了 SOAP 接口,这与前一个例子并没有大的不同,只是这次为了清晰起见,将此接口放在一个单独的单元里实现。唯一区别较大的是此接口中的方法 GetEmployee 返回了一个自定义数据类型: TEmpInfo 。

7.在 SvrWMMain 单元中加入 SOAP 实现类,完整的单元内容如下:

unit SvrWMMain;

interface

uses

SysUtils, Classes, HTTPApp, WSDLPub, SOAPPasInv, SOAPHTTPPasInv,

SoapHTTPDisp, WebBrokerSOAP;

type

TWebModule2 = class(TWebModule)

HTTPSoapDispatcher1: THTTPSoapDispatcher;

HTTPSoapPascalInvoker1: THTTPSoapPascalInvoker;

WSDLHTMLPublish1: TWSDLHTMLPublish;

private

{ Private declarations }

public

{ Public declarations }

end;

var

WebModule2: TWebModule2;

implementation

uses WebReq, InvokeRegistry, SvrDataType, SvrSoapIntf, SvrDataMod;

{$R *.DFM}

Type

TSoapEmployee = class( TInvokableClass, ISoapEmployee )

Protected

Function GetEmployee( aEmpNo : Integer ) : TEmpInfo; StdCall;

End;

{ TSoapEmployee }

Function TSoapEmployee.GetEmployee(aEmpNo: Integer): TEmpInfo; StdCall;

Begin

Result := TEmpInfo.Create;

If ( Not Assigned( DataModule2 ) ) Then

DataModule2 := TDataModule2.Create( Nil );

Try

DataModule2.SQLConnection1.Open;

With DataModule2.SQLDataSet1 Do

Begin

ParamByName( 'EMP_NO' ).AsInteger := aEmpNo;

Open;

If ( Not Eof ) Then

Begin

Result.Name := FieldByName( 'FULL_NAME' ).AsString;

Result.Phone := FieldByName( 'PHONE_EXT' ).AsString;

End

Else

Begin

Result.Name := '';

Result.Phone := '';

End;

Close;

End;

DataModule2.SQLConnection1.Close;

Finally

DataModule2.Free;

DataModule2 := Nil;

End;

End;

initialization

WebRequestHandler.WebModuleClass := TWebModule2;

InvRegistry.RegisterInvokableClass( TSoapEmployee );

end.

这里接口的实现类 TSoapEmployee 的定义与实现与前一例子类似。 GetEmployee 的实现也不复杂:首先,如果未创建 DataModule2 的实例(需要在 Project|Options 中将 DataModule2 从自动创建列表中移去)则创建一个 DataModule2 的实例;然后连接到数据库,查询指定员工号的员工信息;最后返回此信息。注意:这里用了 dbExpress ,有些地方与 BDE/ADO 不太一样,如不能使用 RecordCount ,只能用 Eof 来判断是否有查询结果。

8.至此完成服务端的全部程序,编译并运行,然后退出即完成 Web App Debugger 应用程序的注册。

启动 Web App Debugger ,再启动浏览器,在地址栏输入: http://localhost:1024/Server.wadSoapDemo2/wsdl/ISoapEmployee 即可浏览其 WSDL 内容,在其中包含了自定义类型的必要信息,但如果前面 SvrDataType 单元中的 TEmpInfo 类的属性不是放在 Published 部分的话,这里将看不到类型信息。下面是这个 WSDL 中的 types 标记部分内容:

<types>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:SvrDataType">

<xs:complexType name="TEmpInfo">

<xs:sequence>

<xs:element name="Name" type="xs:string"/>

<xs:element name="Phone" type="xs:string"/>

</xs:sequence>

</xs:complexType>

</xs:schema>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:WSDLSoap">

<xs:complexType name="TWSDLSOAPPort">

<xs:sequence>

<xs:element name="PortName" type="xs:string"/>

<xs:element name="Addresses" type="ns3:TWideStringDynArray"/>

</xs:sequence>

</xs:complexType>

</xs:schema>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:Types">

<xs:complexType name="TWideStringDynArray">

<xs:complexContent>

<xs:restriction base="soapenc:Array">

<xs:sequence/>

<xs:attribute ref="soapenc:arrayType" n1:arrayType="xs:string[]"

xmlns:n1="http://schemas.xmlsoap.org/wsdl/"/>

</xs:restriction>

</xs:complexContent>

</xs:complexType>

</xs:schema>

</types>

从上面这一段 WSDL 中可以看出服务端导出了三个“复杂类型” -- complexType : TEmpInfo, TWSDLSOAPPort, TWideStringDynArray ,其中除了 TEmpInfo 是我们自己定义的数据类型以个,另两个是 Delphi 内部定义使用的类型,在客户端导入 WSDL 时我们会再看到它们的。

再来看客户端的实现:

1.New|Application 新建一个普通的 VCL 应用程序;

2.SaveAll , Unit1 命名为 ClnMain , Project1 命名为 Client ;

3.在 Form1 上放上 HTTPRIO1, Edit1, Button1, Label1, Label2 等控件,如下图:

其中 Edit1 的 Text 设置为 1 , Button1 的 Caption 设置为 GetEmployee , HTTPRIO1 的 URL 属性设置为: http://localhost:1024/Server.wadSoapDemo2/soap ;

4.New|Web Services|Web Services Importer ,与前一例子相似,只是导入的 URL 改为: http://localhost:1024/Server.wadSoapDemo2/wsdl/ISoapEmployee ;

5.如果服务端的 WSDL 如前面所述的那样,则将导入三个单元,分别包含了 TWSDLSOAPPort、 TEmpInfo、 ISoapEmployee ,其中 ISoapEmployee 是我们所认识的 SOAP 接口单元, TEmpInfo 是我们在服务端定义的数据类型, TWSDLSOAPPort 是 Delphi 内部定义的一个数据类型,我们曾在服务端的 WSDL 中看到过这个类型。 Save All ,将 TWSDLSOAPPort 的单元保存为 ClnSoapPort ,将 TEmpInfo 保存为 ClnDataType ,将 ISoapEmployee 保存为 ClnSoapIntf 。注意要将 ClnSoapIntf 单元中的 Uses 中的两个名为 UnitN 的单元相应改为 ClnSoapPort 和 ClnDataType 。由于这三个单元的内容都不需要改变,只要服务端是正确的,可以不必了解这三个单元的内容(特别是 ClnSoapIntf 和 ClnDataType 与服务端的相应单元基本相同),所以这里也就不列出它们的内容了。

6.双击 Button1 输入下面的代码:

procedure TForm2.Button1Click(Sender: TObject);

Var

ei : TEmpInfo;

begin

ei := ( HTTPRIO1 As ISoapEmployee ).GetEmployee( StrToInt( Edit1.Text ) );

If ( Assigned( ei ) ) Then

Begin

Label1.Caption := ei.Name;

Label2.Caption := ei.Phone;

End;

end;

7.编译运行,在 Edit1 中输入"1"或其它数据库中没有相应记录的员工号,按 Button1 , Label1 和 Label2 都将显示空;输入"2"或其它数据库中有记录的员工号,则将在 Label1 中显示员工全名,在 Label2 中显示此员工的电话号码,如下图:

做过一遍再看这个例子也不是那么复杂的。

猛禽 Jun.20-01, Oct.20, Oct.24

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有