Delphi開(kāi)發(fā)和調(diào)用的webservice
在用delphi編寫webservice時(shí)遇到的第一問(wèn)題就是如何編寫,怎么寫?上網(wǎng)查了一下,內(nèi)容還是聽(tīng)不少的,當(dāng)然通過(guò)多方資料的比較,了解到通過(guò)file--new----other----webservice可以建立并測(cè)試webservice,但是我的delphi中卻沒(méi)有這個(gè)選擇,于是很迷茫,問(wèn)了一下“大牛”程序員才知道,是因?yàn)槲业膙cl自帶的包被卸掉了,于是從delphi的安裝文件的bin目錄中找到了相應(yīng)的包,安裝完成后開(kāi)始webservice的編寫和測(cè)試。
下面說(shuō)明如何編寫用于IIS發(fā)布的ISAPI類型的WebService。
1、 New|Other|WebServices|SOAP Server Application|這里先選擇建立Web App Debugger類型的WebService,因?yàn)檫@種類型的WebService便于調(diào)試,當(dāng)我們調(diào)試好它,準(zhǔn)備發(fā)布時(shí)再將此類型轉(zhuǎn)換為ISAPI類型。
2、 選擇Web App Debugger后,隨便輸入一個(gè)ClassName,這里我們輸入“Test”
3、 隨后Delphi會(huì)詢問(wèn)你是否建立接口單元,選擇是,然后輸入接口的名字,我們輸入Main,Delphi將自動(dòng)建立接口單元(名字為你輸入的接口名+Intf結(jié)束,即MainIntf)和實(shí)現(xiàn)接口的單元(名字為你輸入的接口名+Impl,即MainImpl)。到此一個(gè)空的WebService已建立好。
4、 接下來(lái)我們將編寫供別人調(diào)用的WebService函數(shù)。在此我們編寫一個(gè)簡(jiǎn)單的例子。打開(kāi)接口單元(MainIntf),在Type后,接口聲明后添加接口函數(shù)
“function GetMsg(AMsg: string): String; stdcall;”,函數(shù)后面必須加上“stdcall”。
如圖:
5、接口函數(shù)的聲明已經(jīng)完成,下面就是要實(shí)現(xiàn)這個(gè)函數(shù)了。打開(kāi)接口實(shí)現(xiàn)單元(MainImpl),
在public中寫上該函數(shù)的聲明,在implement后寫該函數(shù)的實(shí)現(xiàn)。如圖:
6、到此,WebService已經(jīng)撰寫完畢。接下來(lái)是調(diào)試。在我們新建的時(shí)候,Delphi已經(jīng)為我們 建立了一個(gè)Unit1和其窗體,在Unit1中引用接口單元(MainImpl),然后在窗體中加一個(gè) 按鈕,在按鈕的單擊事件中調(diào)用剛才寫的WebService函數(shù)就可以調(diào)試了,代碼如圖:
或者使用THTTPRIO控件
7、調(diào)試成功后就可以轉(zhuǎn)類型了,將Web App Debugger類型轉(zhuǎn)換為ISAPI類型其實(shí)很簡(jiǎn)單,我們先重新建一個(gè)ISAPI類型的WebService項(xiàng)目,依次選擇New|Other|WebServices|SOAP Server Application|ISPA/…,提示是否創(chuàng)建接口時(shí)選擇“是”,然后輸入和剛才一樣的接口名,接著保存好,然后將調(diào)試成功的Web App Debugger類型的WebService項(xiàng)目中的接口單元和接口實(shí)現(xiàn)單元復(fù)制替換掉剛剛創(chuàng)建的ISAPI類型項(xiàng)目中的接口單元和接口實(shí)現(xiàn)單元,然后打開(kāi)ISAPI類型的WebService,編譯生成dll。至此ISAPI類型的WebService建立成功。
8、將ISAPI類型的WebService發(fā)布到IIS上。在IIS中新建站點(diǎn),新建時(shí)將執(zhí)行權(quán)限設(shè)置成“腳本和可執(zhí)行文件”,將WebService整個(gè)項(xiàng)目拷貝到站點(diǎn)文件夾下,啟動(dòng)站點(diǎn),該WebService就算發(fā)布成功了,如果IIS是6.0以上的注意在Web服務(wù)擴(kuò)展中將“所有未知ISAPI擴(kuò)展”設(shè)置為允許,
具體設(shè)置可參見(jiàn)IIS幫助文檔。如圖:
9、如何用Delphi調(diào)用剛才寫的WebService。在瀏覽器中輸入剛才站點(diǎn)的路徑,如: http://127.0.0.1/project2.dll,瀏覽器轉(zhuǎn)到項(xiàng)目所在文件夾,如圖:
打開(kāi)如下圖所示的的dll描述頁(yè)面,該dll中有一個(gè)個(gè)接口函數(shù)GetMsg,它們都是供別人調(diào)用的接口函數(shù)。點(diǎn)擊WSDL打開(kāi)WSDL描述頁(yè)面,此時(shí)復(fù)制該頁(yè)面的網(wǎng)址http://127.0.0.1/project2.dll/wsdl/IMain,這個(gè)網(wǎng)址是我們要用到的。
10、 得到網(wǎng)址后,新建一個(gè)Application,我們就在這個(gè)Application中調(diào)用剛才的WebService。依次點(diǎn)擊New|Other|WebServices|WSDL Import,如后提示輸入網(wǎng)址,我們輸入剛才復(fù)制的網(wǎng)址,點(diǎn)擊next,finish,此時(shí)Delphi將自動(dòng)添加一個(gè)單元,該單元就是調(diào)用WebService的單元,有了這個(gè)單元我們就可以調(diào)用WebService了。在Unit1中引用該單元,再添加一個(gè)按鈕,在按鈕的單擊事件中聲明一個(gè)接口對(duì)象,然后調(diào)用自動(dòng)生成單元中的GetMainIntf(該方法是自動(dòng)生成的)函數(shù)給這個(gè)接口對(duì)象賦值,然后就可以用這個(gè)接口對(duì)象調(diào)用接口函數(shù)了。
注意:如果前臺(tái)使用THTTPRIO控件來(lái)連接,
把http://127.0.0.1/project2.dll/wsdl/IMain賦給wsdlLocation屬性,而不是賦給URL屬性
*******************************************************
Delphi實(shí)現(xiàn)WebService帶身份認(rèn)證的數(shù)據(jù)傳輸
WebService使得不同開(kāi)發(fā)工具開(kāi)發(fā)出來(lái)的程序可以在網(wǎng)絡(luò)連通的環(huán)境下相互通信,它最大的特點(diǎn)就是標(biāo)準(zhǔn)化(基于XML的一系列標(biāo)準(zhǔn))帶來(lái)的跨平臺(tái)、跨開(kāi)發(fā)工具的通用性,基于HTTP帶來(lái)的暢通無(wú)阻的能力(跨越防火墻)。
WebService給我們的軟件開(kāi)發(fā)帶來(lái)了諸多好處,但是有一點(diǎn)還是必須要考慮到的,那就是安全問(wèn)題。提供Service的一方要控制用戶的限制訪問(wèn),就要對(duì)來(lái)訪的用戶進(jìn)行身份驗(yàn)證。驗(yàn)證成功則繼續(xù)提供服務(wù),否則就觸發(fā)無(wú)權(quán)訪問(wèn)的異常,返回給客戶。那么現(xiàn)在我們要解決的問(wèn)題是這樣的:用戶的身份認(rèn)證信息如何在調(diào)用主要服務(wù)前發(fā)送到服務(wù)方,從而進(jìn)行驗(yàn)證?
在WebService中,用戶身份認(rèn)證信息可以在客戶端通過(guò)soap頭(soap header)進(jìn)行傳送。在WebService服務(wù)端的編寫中,需要對(duì)soap頭進(jìn)行處理,這個(gè)處理過(guò)程就是提取Soap Header中的用戶認(rèn)證信息進(jìn)行驗(yàn)證。下面就來(lái)看看在Delphi中這個(gè)身份認(rèn)證是如何實(shí)現(xiàn)的。
一、 自定義的Header類
你需要定義一個(gè)用來(lái)存放認(rèn)證信息的類,這個(gè)類繼承于TSoapHeader。
TAuthHeader = class(TSOAPHeader)
private
FUserName: WideString;
FPassWord: WideString;
published
property UserName: WideString read FUserName write FUserName;
property PassWord: WideString read FPassWord write FPassWord;
end;
這個(gè)類包含了用戶名和密碼兩個(gè)屬性,當(dāng)然你可以根據(jù)情況增加更多的信息。
再說(shuō)一下這個(gè)類是在哪定義的,它是定義在服務(wù)端的接口聲明單元。服務(wù)發(fā)布以后,生成的WSDL中會(huì)有這個(gè)類的定義,這樣在客戶端用WSDL Importer導(dǎo)入接口單元的時(shí)候,這個(gè)類也會(huì)自動(dòng)生成,當(dāng)然你還要在服務(wù)端對(duì)這個(gè)類進(jìn)行注冊(cè):
InvRegistry.RegisterHeaderClass(TypeInfo(ISoapAuth), TAuthHeader);
RemClassRegistry.RegisterXSClass(TAuthHeader);
ISoapAuth是服務(wù)端提供的服務(wù)接口。
二、 客戶端發(fā)送Header
我們還假設(shè)ISoapAuth是服務(wù)端提供的服務(wù)接口,它提供了GetInfo()這么一個(gè)服務(wù)。
客戶端程序片段:
procedure TClientForm.GetInfoButtonClick(Sender: TObject);
var
aIntf: ISoapAuth;
Headers: ISOAPHeaders;
H: TAuthHeader;
Begin
aIntf := (HTTPRio as ISoapAuth);
H := TAuthHeader.Create;
H.UserName := ‘piao’ ; //這里只是舉個(gè)例子
H.PassWord := ‘840717’;
Try
Headers := (aIntf as ISOAPHeaders);
Headers.Send(H); //發(fā)送Soap Header
aIntf.GetInfo; //調(diào)用服務(wù)
finally
aIntf := nil;
H.Free;
End;
end;
客戶端的工作就是這些了,能否調(diào)用服務(wù)還要看服務(wù)端的處理結(jié)果了。
三、 服務(wù)端接收處理Header
服務(wù)端程序片段:
function TSoapAuth.GetServerInfo: WideString;
var
Headers: ISoapHeaders;
H: TAuthHeader;
begin
Headers := Self as ISoapHeaders;
Headers.Get(TAuthHeader, TSoapHeader(H)); //先獲取SoapHeader
try
if H = nil then //SoapHeader 為空
raise ERemotableException.Create('No authentication header')
else
if not CheckUser(H.UserName, H.PassWord) then //驗(yàn)證失敗
raise ERemotableException.Create('No acess to call on service!');
finally
H.Free;
end;
Result := 'Hello World!';
end;
以上,TSoapAuth是繼承于TInvokableClass 實(shí)現(xiàn) ISoapAuth 的類。
CheckUser()是用來(lái)驗(yàn)證用戶是否具有訪問(wèn)權(quán)限的函數(shù),在服務(wù)端定義。
這只是個(gè)簡(jiǎn)單的返回字符串的服務(wù)。
四、 對(duì)訪問(wèn)WebService的用戶的狀態(tài)的探討
事實(shí)上客戶端在每次調(diào)用服務(wù)端的服務(wù)接口時(shí)會(huì)重新生成一個(gè)對(duì)象,發(fā)送請(qǐng)求,然后接收返回結(jié)果,整個(gè)調(diào)用過(guò)程結(jié)束后這個(gè)對(duì)象就被釋放。所以可以說(shuō)WebService是個(gè)無(wú)狀態(tài)的對(duì)象,也就不存在用戶是否登陸的說(shuō)法。這樣的結(jié)果使得我們每次調(diào)用服務(wù)時(shí)就必須做一次用戶認(rèn)證(這個(gè)認(rèn)證可能是查詢數(shù)據(jù)庫(kù)比對(duì)),是比較浪費(fèi)時(shí)間和資源的。
如果一定要在服務(wù)端保存用戶的登陸狀態(tài),那么可以在服務(wù)端加一個(gè)LogIn()的函數(shù)。當(dāng)用戶第一次訪問(wèn)服務(wù)時(shí),調(diào)用LogIn()記錄下用戶的狀態(tài)信息,并且賦給這個(gè)用戶在一段時(shí)間內(nèi)無(wú)限制(是指不必經(jīng)過(guò)CheckUser這個(gè)過(guò)程)訪問(wèn)服務(wù)的權(quán)限,當(dāng)這段時(shí)間過(guò)后,用戶的登陸狀態(tài)被釋放掉,必須重新登陸才能繼續(xù)調(diào)用服務(wù)。
至于這個(gè)用戶狀態(tài)信息如何在服務(wù)端保存,就可能有幾種方法了。一是用文件形式保存(xml或ini),二是數(shù)據(jù)庫(kù)保存,三是用程序中的變量保存(可以在程序中定義一個(gè)UserList的變量來(lái)記錄用戶的狀態(tài)信息)。
|
|