Web Service 概念的引入帶來了一種全新的、強有力的方法,用于在 Internet 上進行方法調用。開發人員現在可以利用“簡單對象訪問協議”(SOAP) 技術,使用戶遠程調用 Internet Information Services (IIS) 服務器上現有 COM 對象的方法。
因為這種新的調用進程是基于消息的,所以它在語言和平臺方面是未知的。意思是說開發人員可用他們最鐘愛的語言來開發客戶機代碼和服務器對象。Microsoft? SOAP Toolkit 使開發人員能夠在 Microsoft Windows? 2000 服務器上使用這項技術快速而容易地在 Web 上引出方法。
引出現有的 COM 對象
在將 Microsoft SOAP Toolkit 添加到新的開發工具庫以后,我想測試的第一項內容就是,SOAP 與我現有的 Web 應用程序結合得怎么樣,以及有多么容易。許多現有的 COM 對象和 Web Service 都通過 Microsoft ActiveX? Data Objects (ADO) 記錄集將信息返回到客戶機。我開發的用來跟蹤我的音樂庫的 Web Service 就屬于這種情況。
一個 SQL Server? 數據庫包含了我關于藝術家和歌曲的記錄。因為 SOAP Toolkit 能夠幫助在 Internet 上引出我的服務(并且幾年前就出現了在 ASP 頁面或簡單客戶機應用程序中訪問此功能的技術),所以我認為,通過 SOAP 將我的音樂庫服務引出是一件相當容易完成的事情。
在安裝了 SOAP Toolkit 并將它的組件注冊以后(有關正確安裝必需的 SOAP 文件的信息,請參閱 SOAP Toolkit 下載包中的 SOAP Toolkit 文檔),我就準備通過 SOAP 引出我的第一個 COM 對象。首先運行 SOAP Toolkit Wizard,并使它指向我的列表中的第一個 COM 對象 — 該對象返回我的音樂庫中音樂家的一個列表(以及其它一些有幫助的方法)。該向導返回下面的屏幕(圖 1)。
圖 1. SOAP Toolkit Wizard
我最感興趣的方法用紅色加以高亮顯示!發生了什么事?
此處顯而易見的問題實際上根本就不成為問題。它只要求對 SOAP 到底是什么,以及它如何調用方法并返回數據有一定的了解。有了這種了解,您就可以完成某些相當強大的編程目標。
請注意位于方法右側的“注”區域中明確顯示:
“用紅色高亮顯示的方法要么返回未知的數據類型,要么包含未知的參數變量類型。如果您一定要選擇它,則會在 SDL 文件中將有問題的類型定為數據類型 'string'?!?/P>
回想一下,我記得這些高亮顯示的方法返回的是 ADO 記錄集。因此,我要么必須為該對象編寫一段包裝代碼,要么必須重寫我的 COM 對象。
深入研究 SOAP 的工作方式證實,SOAP 的意圖就是在對分布式對象技術沒有任何共享理解的情況下傳遞消息。ADO 記錄集對象是特定于 Microsoft COM 技術的。因為 SOAP 的一個主要目標就是平臺和技術之間的互操作性,所以我們的返回 SOAP 程序包不可能返回 ADO 記錄集這樣的對象。
為了說明這種基于消息的技術,讓我們進一步考察實際的 SOAP 程序包到底返回什么。下面的 SOAP 程序包返回值取自 SOAP Toolkit 的 Microsoft Visual Basic? 樣例,該樣例調用方法 GetServerTime:
正如我們在上面的樣例代碼節選中看到的那樣,SOAP 的返回值是基于 XML 文本的消息。不可能通過這種范例返回像 ADO 記錄集這樣的對象。另一方面,過去我已將 ADO 記錄集轉換為 XML;那么采用什么辦法來改變這種情況呢?實際上,我可以將 ADO 記錄集轉換為 XML,并通過 SOAP 將它傳遞給我的客戶機!簡單的返回值可能類似以下代碼:
...end so on |
這樣我們就發現了一種方法,使用 SOAP 和一點小創新就可以返回某個對象的數據,如 ADO 記錄集。在下一節,我們將探索不同的方法來完成這一任務。
通過選擇方法 GetAllArtists 并選擇使用 ASP 解決方案,我將生成我的服務所必需的源文件。在下一節中討論各種可能的解決方案時,將會用到這些文件。
注:SOAP Toolkit 文檔更詳細地說明了使用 ASP 解決方案與使用 ISAPI 解決方案之間的差異。
可能的解決方案
將記錄集打包
雖然下面的每個解決方案都為我的問題提供了解決方案,但每個方案都各有其優缺點,我將在下面對此進行詳細說明。
我們已經用 SOAP Toolkit Wizard 創建了源文件?,F在我們將看一看它所生成的 ASP 接口文件(因為我們選擇了使用 ASP 監聽程序解決方案)。之所以選擇使用 ASP 解決方案,是因為它具有這樣的靈活性,即它允許在 COM 對象完成的處理之前和之后添加其它代碼。
注:相比之下,ISAPI 監聽程序解決方案最適合調用不需要其它代碼處理的 COM 對象的情況,而我們現在需要進行這種處理。雖然 ISAPI 監聽程序不像 ASP 監聽程序那樣靈活,但它能夠提供比 ASP 監聽程序更好的性能。另外,如果我們能夠修改現有的 COM 對象來返回 XML,或者編寫另一個 COM 對象來包裝現有對象的功能,并使該包裝對象返回 XML,則 ISAPI 解決方案可能會是更好的選擇。
向導生成的函數如下所示:
Public Function GetAllArtists () Dim objGetAllArtists Set objGetAllArtists = Server.CreateObject("Radioweb.clsSongs") GetAllArtists = objGetAllArtists.GetAllArtists() '在此處插入其它代碼 Set objGetAllArtists = NOTHING End Function |
請記住,雖然 GetAllArtists 將返回一個 ADO 記錄集,但是因為這并不是一種符合 SOAP 的數據類型,所以 SOAP Toolkit Wizard 會指定缺省的返回數據類型 "string"。因此,為了返回我們的數據,該字符串返回值將必須是記錄集的一種 XML 表示。
現在我將概要介紹兩種不同的解決方案。解決方案 1 實現了一個單獨的函數,將記錄集轉換為一種 XML 表示。解決方案 2 則使用 ADO 記錄集的 Save 方法,通過 ADO Stream 對象將數據保持為 XML 格式。
本文的后半部分會說明每種解決方案的優缺點。
解決方案 1 打包對于第一種解決方案,我們將實現“粘合代碼”,以方便從記錄集到 XML 字符串的轉換,如下所示:
Public Function GetAllArtists () Dim objGetAllArtists Set objGetAllArtists = Server.CreateObject("Radioweb.clsSongs") GetAllArtists = GetXMLFromADORS(objGetAllArtists.GetAllArtists()) '在此處插入其它代碼 Set objGetAllArtists = NOTHING End Function |
其中 GetXMLFromADORS 如下所示:
Public Function GetXMLFromADORS(ByVal objADORS) Dim objField Dim strXMLString Dim strFieldName Dim strFieldValue strXMLString = " Do while not objADORS.EOF |
GetXMLFromADORS 函數將不會像 ADO Save 方法那樣強健,但它不要求在客戶機上安裝 Microsoft Data Aclearcase/" target="_blank" >ccess Components (MDAC)。其原因是,客戶機只須分析簡單的 XML 結構,而不必將 XML 轉換為 ADO 記錄集。
我還實現了 CDATAit 函數,它將防止插入可能形成畸形 XML 的字符串。在調用過程內部可根據需要使用此邏輯,GetXMLFromADORS 函數的情況也是這樣。在上面的示例中,我只對 ">"、"<" 和 "/" 進行了檢查。
Private Function CDATAit(ByVal strData) CDATAit = "" End Function |
GetAllArtists 函數現在將返回類似下面的結果:
GetAllArtists 函數現在返回了形式良好的 XML 記錄集。請注意,我們的 XML 中不存在任何 CDATA 區域。如果任何藝術家的名字包含了我們所查找的字符("/"、"<" 或 ">"),CDATAit 函數就會將它們包裝在 CDATA 區域,以確保形成形式良好的 XML。
我們必須進行的唯一額外更改就在向導所生成的“服務描述語言”(SDL) 文件中。該文件是用來描述我們正在引出的 Web Service 的 XML 文件。它將位于您指示向導將您的文件保存在其下的同一文件夾中。必須修改向導為我們的返回數據生成的數據類型 — 以便“遠程對象代理引擎”(ROPE)能夠按 XML 正確分析數據,需要指明數據類型是一種 XML 方案。請注意,客戶機代碼仍將把返回數據視為由某種 XML 結構表示的字符串變量。
在這種情況下,將生成如下所示的 SDL 文件:
將需要修改描述數據返回值的部分。您將注意到,這在我們的 SDL 文件中是這樣描述的:
<ELEMENT name="GetAllArtistsResponse"> <ELEMENT type="dt:string" name="return" /> </ELEMENT> |
我們的返回值不再是由 SDL 所描述的字符串,而是表示記錄集的一個結構。因此,對此文件的修改需要將此返回值表示為一個結構,而不是一個字符串??梢酝ㄟ^將該文件更改為以下形式來完成這一工作:
現在請注意,我們是將 'return' 元素作為一種 'MyReturnStruct' 類型返回,而不是作為一個字符串返回。'MyReturnStruct' 是我們將要定義的一種結構元素。它將是我們返回的結構的父結構。請記住,該結構必須具有如下的形式:
</RECORD> .. .. .. |
因此,'MyReturnStruct' 是一種定義如下的元素:
<ELEMENT name="MyReturnStruct"> >ELEMENT type="ArtistStruct" name="RECORD" /> </ELEMENT> |
請注意,'MyReturnStruct' 具有一個名為 'RECORD' 的元素,其類型進一步定義為 'ArtistStruct'。這是必需的,因為子元素
然后將 'ArtistStruct' 定義為如下形式:
<ELEMENT name="ArtistStruct"> <ELEMENT type="dt:string" name="artist" /> </ELEMENT> |
其中,數據類型為 'string' 的元素 'artist' 代表結構中的單個藝術家。
在研究用來分析這些數據的方法之前,讓我們考察一下能夠獲得相同結果的另一種方法。
解決方案 2 打包
將記錄集轉換為形式良好的 XML 字符串的另一種方法是,使用 ADO 將記錄集保存到作為目的地的一個 ADO Stream 對象中,并保持為 XML。
注: MSDN Magazine 就這一主題有一篇更為深入的文章,"使用 ADO 來創建基于 XML 的記錄集",作者為 Don Box (http://msdn.microsoft.com/library/default.asp?URL=/library/periodic/period00/com0700.htm)(英文)。
按這種方式返回記錄集的粘合代碼稍微容易一點。我已在 ASP 接口文件中實現了以下函數:
Public Function GetXMLStreamFromADORS(ByVal objADORS) Dim oStream 'As ADODB.Stream Set oStream = Server.CreateObject("ADODB.Stream") objADORS.Save oStream, adPersistXML GetXMLStreamFromADORS = CDATAit(oStream.ReadText) Set oStream = Nothing End Function |
我已在此 ASP 文件的開頭定義了以下常數:
CONST adPersistADTG = 0 CONST adPersistXML = 1 |
只包括了這兩個常數,因為它們是僅會用到的兩個常數。如果您選擇了 Microsoft Visual Studio? 6.0,則您也可以使用它附帶的 adovbs.inc 文件。
現在,應該通過修改函數 GetAllArtists() 的代碼,使其實現新的幫助器函數 GetXMLStreamFromADORS,如下所示:
Public Function GetAllArtists() Dim objGetAllArtists Set objGetAllArtists = Server.CreateObject("Radioweb.clsSongs") GetAllArtists = GetXMLStreamFromADORS(objGetAllArtists.GetAllArtists()) 'Insert additional code here Set objGetAllArtists = NOTHING End Function |
之所以必須添加 CDATAit 函數,是因為必須將此 XML 字符串表示為一個字符串,以便通過 ROPE 正確地將其返回。我們也可以像在解決方案 1 中那樣修改 SDL,但因為這種結構更難于解釋和預測,所以它可能非常令人厭煩并容易出錯。通過使用 CDATAit 函數,我們可以用數據類型 'string' 在 CDATA 區域返回該結構。此外,我們還可以在將來的記錄集實現中使用這些函數,而且不必修改 SDL 文件。
該函數實際上通過 ADO Stream 對象為我們返回記錄集的 XML 表示,如下所示:
我們隨后將按字符串檢索數據,就像在我們的 SDL 中定義的那樣。請記住,我們的 SDL 是按如下方式來定義返回值的:<ELEMENT name="GetAllArtistsResponse"> <ELEMENT type="dt:string" name="return" /> </ELEMENT> |
因為我們的數據實際上包裝在 CDATA 區域中,所以可以保證,當從 Stream 對象返回時,我們的數據是合法的 XML。
注: 這些示例中所使用的函數,如 GetXMLStreamFromADORS 和 GetXMLFromADORS,可以很容易地放入一個 include 文件中,實現類似 COM 對象(返回 ADO 記錄集)的 ASP 接口文件都可包括該文件。在您的 Web Service 的整個生存期中,這種可重用性都將有助于今后的開發。
檢索記錄集
既然已成功地將返回數據打包,那么必須在客戶機應用程序中正確分析這些數據。根據您決定使用哪一種上述解決方案,可以有幾種不同的方法來實現這一點。我們將單獨考察每個解決方案。
解決方案 1 檢索
解決方案 1 中返回的數據是記錄集的一種簡單 XML 數據表示?,F在,我們將使用 Microsoft XML Document Object Model (DOM) 來檢索此數據,以便在內部循環處理每一條記錄,XML 對這一過程的表示如下:
Private Sub Command1_Click() Dim aryArtists() As String Dim oRope as New Rope.Proxy oRope.LoadServicesDescription icURI, "http://MyServer/GetArtists.xml" GetArtistsFromXML oRope.GetAllArtists(), aryArtists() '現在 aryArtists() 是包含我們的藝術家的一個數組 Set oRope = Nothing
|
Command1_Click() 方法使用 ROPE 調用服務。因為該服務按解決方案 1 中指定的方式返回數據,所以現在就可以傳遞包含在我們的 XML 字符串中的返回數據。我們按值傳遞該字符串,并按引用傳遞一個將被置入 Public Sub GetArtistsFromXML 中的空數組。此過程置入數組并將它返回給調用過程。數組變量 aryArtists() 現在包含我們的藝術家的一個數組。
解決方案 2 檢索
為了將此 XML 數據返回給一個 ADO 記錄集,我們需要將數據加載到 XML DOM 中,然后使用 ADO 記錄集的 OPEN 方法創建藝術家的記錄集:
Private Sub Command1_Click() Dim oRS As ADODB.Recordset Dim oRope as New Rope.Proxy oRope.LoadServicesDescription icURI, "http://MyServer/GetArtists.xml" Set oRS = CreateADORSFromXML (oRope.GetAllArtists()) '現在 oRS 是包含我們的藝術家數據的一個分離的 ADO 記錄集 Set oRope = Nothing
oXML.loadXML sXML Set CreateADORSFromXML = oRS |
在 Command1_Click() 事件中,ROPE 對象調用我們的方法,該方法會就像在解決方案 2 中那樣返回我們的 XML 字符串。通過調用 CreateADORSFromXML 函數,就可以設置一個對象引用從 SOAP 返回數據創建一個分離的記錄集。
我們的解決方案的優缺點雖然每個解決方案都為我們的問題提供了答案,但有時您可能想使用的是某一種方案,而不是另一種。下面的兩個表提到了幾個示例。
表 1. 解決方案 1 的優缺點
優點 |
缺點 |
因為不使用 ADO,所以不要求在客戶機上安裝 MDAC。因此,開發人員不必將 MDAC 打包到應用程序的發行包中。 | GetXMLFromADORS 函數是用來處理簡單記錄集的。更復雜的記錄集結構可能要求使用 ADO 為您的解決方案重新創建 XML。用您的數據結構測試此函數,以確保能夠正確表示您的記錄集。 |
GetXMLFromADORS 函數必須分析每個字段中的文本來構建 XML 表示。對于大型記錄集,這可能是一個時間較長的過程,因為該函數要查找可能導致畸形的 XML 的字符,以便“凈化”文本。 |
表 2. 解決方案 2 的優缺點
優點 | 缺點 |
將 COM 對象返回的原始記錄集從 XML 重新構建成一個 ADO 記錄集對象。這樣,開發人員就可以針對 ADO 記錄集編寫代碼,就好象它是原始對象一樣。 | 必須在客戶機上安裝 MDAC。這將增加您的安裝程序和分發文件的大小。 |
與 GetXMLFromADORS 函數相比,能夠更準確地表示 XML 記錄集的 ADO 持久性。 | |
數據的真正面對對象的表示。 |
一種解決方案可能比另一種工作得更好,這取決于您的需要。如果您無法控制您的客戶機是否安裝 MDAC,則您可能要選擇解決方案 1 的方法。如果數據的真正的對象表示是必需的,而且已經知道客戶機上安裝了 MDAC,則應該選擇解決方案 2。
要考慮的問題當實現這些類型的轉換時,這兩種解決方案都可能會產生一些問題。這些問題既適用于數據打包,也適用于數據檢索。還可能出現其它問題,這取決于數據的完整性、結構和大小等等。
數據字段
當使用解決方案 1 時,數據字段不能包含可能導致畸形 XML 的字符(<、>、/,等等)。函數 GetXMLFromADORS 使用邏輯
"<" & strFieldName & ">"這可能導致畸形的 XML 表示。像 <、> 和 / 這樣的字符,可能導致 XML 分析程序認為這些字段是實際的 XML 標記。在這種情況下,將不會正確表示 XML 數據,可能形成畸形的 XML 數據,并很可能導致分析程序失敗。
此外,在一個記錄中返回 HTML 數據時一定要小心。這種數據也可能危及返回的 XML 數據的完整性。發生這種情況的主要原因是,HTML 通常是畸形的。用這種方式插入 HTML 將導致返回畸形的 XML。如果發生這種情況,ROPE 將報告一個錯誤,指出產生了一個錯誤的 SOAP 返回值。要解決這個問題,應該將 HTML 數據封裝在一個 CDATA 區域中。
二進制數據
如果二進制數據是以 Base64 編碼的,則可以通過 SOAP 傳送它。因為 SOAP 是基于文本的,所以將使用 SOAP 把二進制數據作為其基于文本的表示加以傳送。該數據在到達客戶機以后,仍可將它解碼為二進制數據。在本實現中不討論 Base64 編碼算法。
大型記錄集
如果使用解決方案 1,則使用這種消息收發類型的大型記錄集返回將證明是耗時的,因為該函數必須分析全部字段值來確保生成形式良好的 XML。因此,在使用解決方案 1 時,一定要測試該解決方案同時滿足服務器和客戶機實現的性能要求。
作為數據類型的記錄集
記錄集并不是“服務描述符語言”(SDL) / SOAP 中的合法數據類型。SOAP Toolkit Wizard 最初在 SDL 中將記錄集表示為一個 "string"。開發人員必須修改 SDL 來表示記錄集的結構。這樣,他就將此數據結構表示為返回數據的真正 XML 表示。
分層記錄集
當以 XML 格式保存分層記錄集(數據的形狀)時,存在兩個局限性。一個局限性是,如果分層記錄集包含了未決更新,就不能保存為 XML;另一個局限性是不能保存參數化的分層記錄集。
ADO 版本
這些示例都是使用 ADO 版本 2.5 構建的。在客戶機和服務器上使用有沖突的版本可能會產生不可預料的結果。
原文轉自:http://www.anti-gravitydesign.com