数据挖掘研究院 電子商務新紀元-WebService With BizSnap
WebService 是今年最熱門的技術。 所有資訊雜誌都免不了添上幾篇文章,
談談WebService 的遠景,或是她所使用的SOAP 標準,大部份的開發工具也都
投入了WebService 戰局,預期WebService 將會為快速泡沫化的電子商務注入
一股新的動力。 號稱最強的RAD開發工具DELPHI 也不例外,DELPHI 6 提供了BizSnap!
其中包含了3個Wizard,7個元件,讓程式師能夠以最直覺最快速的方式開發及除錯
WebService Server&Client 應用程式,撇開尚未上市的Visual Stdio .NET 不談,
DELPHI 6 可以說是開發WebService 最快速的工具。除了開發工具外,與WebService
相關的SOAP 及WSDL 規格也正以極快的速度成長,預計規格的變動是無法避免的。
雖然目前SOAP 1.1 已經定案,可是參與定制SOAP 的各大廠商實作上都還有差異。
DELPHI 6 所實作的SOAP 及WSDL 比較貼近Sun,IBM 陣營,所以用DELPHI的BizSnap 來呼叫
Java WebService 應該不會有太大的困難,但如果對象是.NET WebService Server 就有點困難度了。
.NET 實作的SOAP 規格預設使用document 方式,而BizSnap 目前只支援rpc,因此與.NET無法溝通,
DELPHI R&D 正在加入document 的支援,相信很快的我們就可以在不更動BizSnap 原始碼的情況下,
正常的呼叫.NET WebService。 跨平台,跨語言是WebService 的主要訴求,希望這些廠商能儘快
解決彼此之間的問題,讓WebService 真正成為我們生活的一部份。 数据挖掘研究院
WebService
就如同字面一般,WebService主要是提供服務。 讓其它人可以撰寫程式或經由網頁存取
你所撰寫的服務,或是將你的服務整合至他們的產品中來減少產品開發的時間,讓產品以
更快的速度出現在市場上。 這同時形成了供應鍊關係,你可以利用別人的服務來減少
產品開發時間及成本,而提供服務的廠商則可以藉由販賣服務來獲得利益,使用者也可以
更快速的享受到新產品。 這會使得整個資訊速度大幅提升,也創造出更多的商機。
WebService 也解決了各平台及程式語言間的差異性,使得語言及平台間能夠使用
一致的資料格式來溝通。 舉個例子來說: 像信用卡請款作業,目前各銀行的請款作業
格式都不相同,因此你必須針對個別銀行開發相容與該銀行相容的程式,
這個程式的流程大概是這樣:
上圖中你可以發現到,應用程式必須從資料庫取出資料,轉成銀行要求的請款文件格式
後送給銀行,銀行再將結果轉成特定格式的文件後傳回我們的應用程式,應用程式還得
解譯資料後才能存入資料庫。 這是目前所有信用卡銀行所使用的請款方式,這種方式既
麻煩又沒有公定的資料標準,程式的撰寫也很花時間。 使用者還必須手動操作文件的匯
出及匯入的動作,這既不聰明也容易出錯,假如銀行提供一個WebService 用來處理請款作業,
那麼情況就大大不同了。
上圖中應用程式只需要使用HTTP+SOAP 文件就可以Invoke(喚起)銀行所提供的WebService,
當WebService 處理了應用程式所傳過來的文件後,將結果以SOAP 文件傳回應用程式,
我們的應用程式就可以將資料存入資料庫。 這比起之前的方法聰明,使用者也不需要接觸到
容易出錯的匯入及匯出文件的動作,程式設計師也可以藉由一致的通訊標準來整合各語言
跟平台,這個技術的關鍵點就在於 SOAP。 簡單的說! SOAP 是一組訊息標準,用來傳遞訊息,
使不同語言撰寫的應用程式可以互相溝通。 下一章我會詳細討論SOAP,現在讓我們回到信用卡
請款範例,雖然我們有一致的文件標準,但是文件的內容還是會有差異性,例如甲銀行需要地址,
乙銀行不需要等問題。 那代表程式設計師又得回到老路了嗎? 或許不! 程式還是得寫,
只是這次你有WSDL Tools 幫助你產生骨架程式,比起原先的工作簡單多了。看下圖:
数据挖掘实验室
支援WebService 的開發工具應該都會附帶一個WSDL Tools,用來產生SOAP Proxy Code。
上圖中開發工具以送出HTTP Request 的方式向WebService 要求WSDL 文件,之後將
傳回結果交給WSDL Parser, WSDL Parser 根據WSDL文件產生出SOAP Proxy Code,
這個SOAP Proxy Code 在DELPHI 中就是Interface 的定義。 你可以使用THTTPRIO 元件
以as 指令取出這個Interface 後,就可以直接呼叫WebService 中的Method。 在Interface定義
中除了Method 的資訊外,還存在了傳入值與傳回值的型別定義,如果傳回值或傳入值是
複雜型態的話,可能還包含複雜型別的定義及處理的程式碼。 這樣就可以解決上述的格式問題了,
你只需經由各家銀行的WSDL 產生個別的Proxy Code 就能夠呼叫個別的WebService 來完成你的工作,
這樣你要做的事真的減少了許多不是嗎? 数据挖掘研究院 数据挖掘研究院
SOAP(Simple Object Access Protocol)
SOAP 是一個訊息標準。 目前WebService 使用她來當作資料交換的標準,
本文不嘗試完整解釋SOAP! 如果你想完整的了解SOAP 的話,請自行找書
或到W3C 上看看規格書,這裡我只簡單的列出幾樣你應該要知道的部份。
SOAP需要一個通訊協定來傳輸訊息,目前的SOAP 1.0,1.1 都偏向於使用HTTP
做為通訊協定。除了HTTP 之外SOAP 也可以使用SMTP,FTP 等其它通訊協定來傳
輸資料,但是目前除了HTTP 外,其它部份的實作都還沒有定論,由於SOAP 的
Request/Response 作業模式跟HTTP 協定很類似,所以目前的實作都以HTTP為主,
相信以後應該會有更多的協定來滿足不同情況的應用。
一個標準的SOAP Request 樣子大概如下(使用HTTP):
POST /Project2.MyService/soap/IMyService HTTP/1.1 Accept: application/octet-stream, text/xml SOAPAction: "urn:MyServiceIntf-IMyService#GetComplexType" ……. 数据挖掘研究院 <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<NS1:GetComplexType xmlns:NS1="urn:MyServiceIntf-IMyService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<NS1:Param1 xsi:type="xsd:string">
1234
</NS1:Param1>
</NS1:GetComplexType>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope> 我們先看POST /Project2.MyService/soap/IMyService這個部份,這裡指定了
要喚起的是那一個WebService Server 程式,以.NET 來說,這可能是一個ASP .NET 網頁,
以DELPHI 來說,就是一個CGI 或 ISAPI程式。 在下面的SOAPAction 段則是指定我們
想呼叫WebService中的那一個Method, WebService Server 會解讀這一段來喚起對應
的Method 程式碼, 不過SOAP 1.1 並未強制一定要有SOAPAction 值,這必須視實作者而定。
在喚起Method 之前,Server 還要解讀Body 區段中的資訊來取得Client 所傳遞的參數及
型別, 以目前的SOAP規格書來看,你只能夠在Body 區段中包含一個Method 的資訊,
這也代表了一次HTTP Request 只能夠喚起一個Method,雖然有人提出了可以在一次的HTTP
傳輸中喚起多個Method 的資料格式,但目前還沒有正式的規格。 在上例中,<NS1:GetComplexType>
就是我們要呼叫的Method資訊封包,這個區段中包含了呼叫GetComplexType Method的參數型別與值,
NS1:Param1 就是這個Method 所需要的參數,"xsi:type="xsd:string" 就是該參數的型別,
當我們將這個Request 送給WebService 後, WebService 就會根據這些資訊執行對應的Service
及 呼叫Method,完成後她會回覆像這樣的訊息給我們:
HTTP/1.1 200 OK 数据挖掘研究院 Content-Type: text/xml Content-Length: 867 Content: 数据挖掘研究院 <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<NS1:GetComplexTypeResponse xmlns:NS1="urn:MyServiceIntf-IMyService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS2="http://www.w3.org/2001/XMLSchema">
<NS1:return href="#1"/>
<NS2:return id="1" xsi:type="NS2:TXSMyComplexType">
<NS2:ServerMessage xsi:type="xsd:string">
Test My Self
</NS2:ServerMessage>
<NS2:InnerString href="#2"/>
</NS2:return><NS2:InnerString id="2" xsi:type="NS2:TXSInnerType">
<NS2:InnerString xsi:type="xsd:string">This is InnerType</NS2:InnerString>
</NS2:InnerString>
</NS1:GetComplexTypeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
這個WebService 使用了兩個Complex Type, 所以看起來有點亂!
不過你還是可以發現與Request 差別不大,我們先看紅色字的部份
GetComplexTypeResponse 代表呼叫的Method傳回封包, Client 端程式
藉由解讀這部份的資訊來取得傳回值跟型別。例子中藍色及深藍色
部份就是呼叫Method 後的傳回值, 理論上SOAP 可以表現相當複雜的資料型態,
以上面這個例子來說,我使用了一個Complex Type:TXSMyComplexType並且在
裡面使用了一個TXSInnerType,因應情況的不同,你也可以傳遞TXSMyComplexType型態
的陣列或更複雜的資料型態來表示資料庫中的資料列。 SOAP Request/Response Message
通常是由開發工具幫你產生及解讀,所以我們接觸SOAP Message 的機會不多,但這不代表
我們不需要了解SOAP,當你的程式發生問題時,這些資訊將會給你相當大的幫助。
除了Request及Response Message 之外,SOAP 也提供Fault 訊息格式,這個訊息會在
Service Server 發生錯誤時傳回Client 端,DELPHI 的Service Server 會自動將Exception
包裝成這種格式後傳回Client 端,Client 端再根據其中的錯誤訊息產生一個例外。 数据挖掘研究院
HTTP/1.1 200 OK Content-Type: text/xml Content-Length: 473 Content: 数据挖掘研究院 <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<SOAP-ENV:faultcode>SOAP-ENV:Server</SOAP-ENV:faultcode>
<SOAP-ENV:faultstring>Test Exception</SOAP-ENV:faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
這應該很易懂,所以我就不再解釋了。
除了這些之外,還有SOAP Header 區段,只是目前的BizSnap 並不能讓我們方便的定義此區段,
在這個區段中可以包含許多自定的資訊,希望以後能夠以BizSnap 來處理這段的資料。
数据挖掘研究院
Web Services Description Language (WSDL)WSDL 是用來描述WebService 資訊的語言,應用程式經由解讀WSDL 來取得有關連結
WebService 的資訊,WSDL 大概分成幾部份:
<service name="IDPServiceservice"> <port name="IDPServicePort" binding="IDPServicebinding"> <soap:address location="http://localhost:1024/Project1.MyDPService/soap/IDPService" /> </port> </service>
藍色部份代表WebService 的名稱,應用程式要連結至WebService 時必須要指定一個Service
跟一個Port, 紅色部份就是Service Port 的描述。 一個Port 必須要有一個SOAP Address Location,
用來描述該Port 的URL 位址,這樣應用程式才能經由這個位址與Service 溝通,
之前提過SOAP 支援多種傳輸方式,而binding 屬性就是描述這些資訊的地方:
<binding name="IDPServicebinding" type="IDPService"> 数据挖掘实验室 <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="SayHello"> <soap:operation soapAction="urn:DPServiceIntf-IDPService#SayHello" /> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:DPServiceIntf-IDPService" /> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:DPServiceIntf-IDPService" /> </output> </operation> </binding>
藍色部份就是binding 的資訊,這個Port 使用HTTP 傳輸協定,並且指定使用rpc為傳輸樣式。
SOAP 目前定義兩種傳輸樣式: rpc 跟document。 DELPHI 目前只支援rpc 的傳輸樣式,
.NET 則兩個都支援,預設值是使用document 模式。 深藍色部份描述了Service 中的Method,
你可以在裡面發現SOAPAction的資訊跟SOAP Body 區段所使用的樣式(Style)。
SOAP 的Body 區段可以使用 encoded/literal 兩種樣式,
一般來說, rpc 通常搭配encoded,document 則搭配literal樣式。
在這裡描述的只有binding information,Method 所需要的參數及型別則在下面這段描述中:
<message name="SayHelloRequest" /> 数据挖掘研究院 <message name="SayHelloResponse"> <part name="return" type="xs:string" /> </message>
籃色部份就是message 的描述,這個描述代表SayHello Method 不需要有傳入值,
其紅色部份則描述了SayHello Method 會有一個傳回值,型別是string。
WSDL 中較重要的資訊大概就是這些了,其它有關於WSDL 的詳細資訊,就請你到www.xml.org 查看。
由於目前各家廠商所實作的標準都略有差異,因此自動產生的Proxy Code 常常需要你介入調整,
希望日後能統一。
(.NET 所產生的WSDL 比其它語言來的複雜,所以我不知道要以誰的為準來解釋.......) 数据挖掘研究院 数据挖掘研究院
DELPHI 的SOAP使用DELPHI 6 來撰寫WebService 可以說既簡單又快速,只要照著On-line Help 操作
大多可以完成你自己的WebService。 有註冊的使用者還可以從Borland Site 傳回一個
Invokable Wizard,這個Wizard 號稱可以讓你60 秒寫出一個簡單的WebService,有註
冊的使用者可別錯過了。 我自己也寫了一個類似的Wizard,文後會提供一個URL
你只要下載後安裝就可以了。 DELPHI 中提供了三個Wizard 來產生WebService 骨架程式,
現在讓我們哂眠@幾個 Wizard來實際撰寫一個WebService Server!
撰寫WebService Server 程式
首先開啟New Items Dialog 後選擇WebService頁,你會看到這幾個Wizard
(其中有兩個是你安裝了我的Wizard 後才有的),SOAP DataModule 是用來產生
與DataSnap 相容的DataModule,SOAP Application則是用來產生一個
WebService Project,WSDL Import則可以讓你經由WSDL 文件產生SOAP Proxy Code。
請選擇Soap Server Application,開一個新WebService Server專案。
接著選擇Server 型態,在這裡我使用的是Web App Debugger,這可以讓我們
利用IDE 來除錯我們的程式,確定後DELPHI 就會為你產生一個骨架程式,
接著我們必須定義我們要提供的 Service Interface跟Implementation 程式碼,
請再開啟New Items Dialog!
請選擇Service Generate Wizard 來產生一個Service 骨架程式。
在Interface Name 區填入DPService 後按確定後你會看到這個Wizard 已經為你產
生了Service 骨架程式,接著請將整個專案存檔,之後我們再為這個骨架程式提供程式碼:
数据挖掘实验室
(紅色部份是加進去的)
(定義部份)
unit DPServiceIntf; // Created with Thor-Mjollnir Service Module Creator 数据挖掘实验室 interface uses
InvokeRegistry; 数据挖掘研究院 type
IDPService = interface(IInvokable)
["{F5AF5412-0882-4C69-8878-4775C1D77555}"]
function SayHello:string; stdcall; //WebService 所提供的Method
end; implementation 数据挖掘实验室 initialization 数据挖掘研究院 InvRegistry.RegisterInterface(TypeInfo(IDPService));
end.
(實作部份)
unit DPServiceImpl;
// Created with Thor-Mjollnir Service Module Creator interface 数据挖掘研究院 uses
InvokeRegistry,DPServiceIntf; type
TDPService = class(TInvokableClass,IDPService)
private
{ Private declarations }
public
function SayHello:string; stdcall;
{ Public declarations }
end; 数据挖掘实验室 implementation
//Method 的實作 数据挖掘研究院 function TDPService.SayHello:string;
begin
Result:="Hello!";
end; initialization
InvRegistry.RegisterInvokableClass(TDPService); 数据挖掘研究院 end.
編譯完成後執行一次,讓她註冊自己,就這樣! 你已經開發了一個簡單的WebService
並提供一個SayHello Method讓Client 端呼叫,接著我們要寫哂盟腃lient 端程式,
在動手之前我們必須要將WebAppDebuger 啟動,你可以在Tools 選單中找到她。 数据挖掘研究院
撰寫Client 端程式
使用DELPHI 中寫Service Client 應用程式就像寫一般程式一樣簡單!
請開啟一個新專案後選擇New Items 的 WebService 頁,
再選擇Web Services Importer Wizard,這個工具可以產生SOAP Proxy Code
也就是Interface 定義。
在這裡填入WSDL 所在的URL:
http://localhost:1024/Project1.MyDPService/wsdl/IDPService
當HTTPRIO 啟動時,HTTPRIO 會根據這設定來取得WSDL 文件,
因此當程式完成後,你可以將WSDL 文件存在本機電腦上減少一些網路流量。
数据挖掘研究院 執行成功後你會有一個跟下面程式一樣的骨架程式,在這個程式中定義了
WebService 所提供的Method,有了這些資訊我們就可以使用Code Insight
及Compiler 的型別檢查功能來減少錯誤,這也是以DELPHI 開發Service 的好處
之一。 数据挖掘研究院
Unit Unit3; 数据挖掘研究院 interface 数据挖掘研究院 uses Types, XSBuiltIns;
type 数据挖掘实验室 IDPService = interface(IInvokable)
["{06D0E720-0D1D-437F-B2BA-8DAB58AF7E30}"]
function SayHello: WideString; stdcall;
end; 数据挖掘研究院 implementation 数据挖掘研究院 uses InvokeRegistry; 数据挖掘研究院 initialization
InvRegistry.RegisterInterface(TypeInfo(IDPService), "urn:DPServiceIntf-IDPService", "); 数据挖掘研究院 end.
將專案存檔後,我們接著加入呼叫WebService 的程式碼,
請到元件盤的WebService 頁拖放一個THTTPRIO 元件到FORM 上
並在她的WSDLLocation 特性中填入WSDL 的URL(之前說的那一個),
之後你就可以在Service 及 Port 特性開窗找到Service Name。
當你使用的是其它語言所撰寫的Service 時請記得在Port 特性值中選用
SOAP 結尾的那一個Port,在Java 及 .NET 中除了SOAP 之外,
還有HTTPGet與HTTPPost 兩種Port,DELPHI 目前只支援SOAP Port。 数据挖掘研究院
接著請再放入一個TButton 元件到Form 上,並撰寫她的OnClick Event,
整個Client端程式碼應該像下面這個程式:
unit Unit1; 数据挖掘研究院
interface 数据挖掘研究院 uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rio, SoapHTTPClient; type
TForm1 = class(TForm)
HTTPRIO1: THTTPRIO;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end; var
Form1: TForm1; implementation
uses Unit2; 数据挖掘研究院 {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage((HTTPRIO1 as IDPService).SayHello);
end;
end.
請注義粉紅字的部份,當我們要呼叫Service 時,我們必須將HTTPRIO 轉型為該Interface,
之後才能呼叫Method,這同時也是DELPHI 強大的地方, DELPHI 使用Runtim Creating VTable
的方式完成這個工作,比起SOAP Tookit 的Late Binding 方便多了,到現在為止! 我們簡簡單單
就寫了一個WebService 跟一個Client 端了,以DELPHI 6 來開發WebService真的很方便,
只是如果我需要傳送複雜的資料的話,例如一個TDataSet 那我應該怎麼作呢?
這有很多種方法可以達到,如果你真的要傳DataSet 的話,SOAP DataModule+DataSnap 也許比較方便,
但其它語言可能無法讀取,除了DataSnap 之外,你還可以選擇使用Complex Type,
而這種方式也可以輕易被其它語言所接受,這是我們下一節會談到的。
数据挖掘研究院
魔法的秘密
如你所見! DELPHI 開發WebService 應用程式真的很快速,且直覺。 但魔法是不會憑空出現的,
在神秘的魔法背後,必定藏著許多的故事。 在BizSnap 背後的技術就有如魔法故事一樣,
精妙且有趣, 當然! 知道這些並不能加快你的開發速度或程式品質,不過她卻賦與你另一種
神奇的力量,使你能夠在程式不能正常咦鲿r,迅速找到解決的方法。
首先我們先看看當Service Server 接收到來自Client AP的要求時的處理方式:
當Client AP 第一次呼叫WebService 時,HTTPRIO 會經由WSDLLocalation 特性先取得WSDL 文件,
因此你在圖中可以看到 Client AP 送出一個HTTP Request 給Service Server,這個訊息會經由
TWebModule 轉送給TWSDLHTMLPublish 接著再由TWSDLHTMLPublish 產生WSDL 文件後傳送給Client AP,
這只有在你指定的WSDLLocation 是連結至Service Server 的URL 時才會發生,假使你預先將WSDL 存
放在本機或是第二次呼叫的話,這個動作就不會發生了。
取得WSDL 文件後,HTTPRIO 就會將Request 轉成SOAP Message傳送至Servcie Server,再由TWebModule
轉送給TSOAPDispatcher,接著經由TSOAPDispatcher解讀SOAP Header中的SOAPAction 資訊後轉送給TSOAPPascalInvoker,
這時TSOAPPascalInvoker 會建立一個THTTPSOAPToPasBind 物件並將SOAP Action傳入後取得Service Class Type,
再哂肨OPToSoapDomConvert 來解出SOAP Message 中的參數後呼叫對應的Service。
當Service 執行完之後,TOPToSoapDomConvert 就會將Method 的傳回值打包成SOAP Response Message後傳回Client AP。
除了表面上看到的這些之外,InvokeRegistry也扮演著相當重要的角色,因為TSOAPPascalInvoker 必須得經由她來取得
Service Interface 與Service Object,之後才能使用TInterfaceInvoker來呼叫對應的Method。
可惜Borland 並沒有公開Invoker 的原始碼,但我們可以想像其中必定包含許多Compiler 層級的技術,BizSnap 目前
還有一些缺點, 那就是程式設計師無法介入這些步驟! 因為這些元件既沒有事件,也不能繼承,希望Borland以後
能解除這個限制. 除了Service Server 之外,Client AP端的魔法也很有趣,讓我們繼續探索BizSnap Client 的魔法:
還記得之前提到的要求WSDL 文件嗎? 在這張圖中我省略掉她了。 當你利用HTTPRIO 來呼叫Service Method時,
HTTPRIO 會到InvokeRegistry 取得相關的SoapAction 資訊,再將參數等資訊交由TOPToSoapDomConvert 轉換
為SOAP Request Mesage後經由THTTPReqResp 送至Service Server,回返後再由TOPToSoapDomConvert 轉換
SOAP Response Message 成為Pascal 型態後存入對應的物件,乍看之下 Client 的步驟似乎沒有Server 複雜,
其實不然! 因為還有HTTPRIO 可以轉型成任何型態這個魔法沒解開,Borland 也沒有公開RIO 的原始碼,不過我想其中
必定有許多的magic code,身為程式設計師,我很好奇! 不過我也能了解Borland 不公開的想法,罷了! 這對我們並
沒有太大的影響。
關於魔法的初級篇就到此打住,讓我們繼續往下看吧。 数据挖掘实验室 数据挖掘研究院
傳送複雜型態資料(Complex Type)
在SOAP 一節中我提到了SOAP 可以傳送複雜的資料,在這裡我們就使用DELPHI
來實作一個可以傳送Complex Type 的WebService。
首先請你開啟Service專案,並啟動New Items,
你可以看到WebService內有一個Complex Type Generate Wizard,請執行她。
接著填入Type Name 跟勾選Generate Dynamic Array,
這會產生Array Type 的定義。
之後她會產生Complex Type 的骨架程式碼 数据挖掘研究院 unit Unit3; // Created with Thor-Mjollnir Complex-Type Module Creator 数据挖掘研究院 interface 数据挖掘实验室 uses InvokeRegistry,Types,XMLSchema; 数据挖掘研究院 type
TXSPerson=class;
TXSPersonArray = array of TXSPerson;
TXSPerson = class(TRemotable)
private
//declare your data member,remember! data member need publish in published section
FName:string;
FAge:Integer;
{ Private declarations }
published
property Name:string read Fname write Fname;
property Age:Integer read Fage write Fage;
{ Public declarations }
end; 数据挖掘研究院 implementation 数据挖掘实验室 initialization RemClassRegistry.RegisterXSClass(TXSPerson, "http://www.code6421.com/XMLSchema", "TXSPerson","); RemTypeRegistry.RegisterXSInfo(TypeInfo(TXSPersonArray),"http://www.code6421.com/XMLSchema","TXSPersonArray"); finalization RemClassRegistry.UnRegisterXSClass(TXSPerson); RemTypeRegistry.UnRegisterXSInfo(TypeInfo(TXSPersonArray)); end. 数据挖掘实验室
將她存為uXSPerson.pas,之後我們還要回到Interface,在定義的Unit 中添加新的Method。
数据挖掘研究院
unit DPServiceIntf; // Created with Thor-Mjollnir Service Module Creator 数据挖掘实验室 interface 数据挖掘研究院 uses
InvokeRegistry,uXSPerson; type
IDPService = interface(IInvokable)
["{F5AF5412-0882-4C69-8878-4775C1D77555}"]
function SayHello:string; stdcall;
function GetPerson:TXSPerson;stdcall;
end; implementation 数据挖掘研究院 initialization 数据挖掘研究院 InvRegistry.RegisterInterface(TypeInfo(IDPService));
end.
定義好Interface 後,我們還要為這個Method 提供實作碼。 数据挖掘研究院 unit DPServiceImpl; // Created with Thor-Mjollnir Service Module Creator 数据挖掘研究院 interface 数据挖掘研究院 uses
InvokeRegistry,DPServiceIntf,uXSPerson; type
TDPService = class(TInvokableClass,IDPService)
private
{ Private declarations }
public
function SayHello:string; stdcall;
function GetPerson:TXSPerson;stdcall;
{ Public declarations }
end; implementation function TDPService.SayHello:string;
begin
Result:="Hello!";
end; 数据挖掘实验室 function TDPService.GetPerson:TXSPerson;stdcall;
begin
Result:=TXSPerson.Create;
Result.Name:="code6421";
Result.Age:=18; //ha!
end; 数据挖掘实验室 initialization
InvRegistry.RegisterInvokableClass(TDPService);
end.
回到Client 端,請你手動在Service Interface 加入GetPerson function的定義,
這個動作並不困難,你只須要將ComplexType Unit Copy 到你的Client 目錄中並use 她就可以了,
接下來你應該就可以呼叫GetPerson 這個Method 了,基本上你的Complex-Type 中也可以包含
Complex-Type,所以組合後可以表現相當複雜的資料型態,除此之外你也可以產生Complex-Type Array
用以表示更複雜的資料,這些都是很簡單的哂茫嘈拍愫芸炀涂梢陨鲜至恕
|