簡介
在本篇文章中,我們將討論如何使用Visual C++開發一個簡單的SOAP用戶端應用程式,我們還將介紹SOAP API的使用。SOAP是互連網上一種非常流行的交換資訊用的協議,由於是為了與HTTP、SMTP和其他的類似協議協同工作的,因此它十分簡單。用它描述的資訊能夠被輕易地通過互連網發送到另外的電腦上,而無需擔心遭到防火牆等網路安全技術的攔截。
在這裡,我們假設讀者已經對SOAP協議有了一定的理解,而且對C++比較精通。如果讀者對SOAP還不熟悉,可以查看相關的資料。我們還假設讀者熟悉COM的使用,特別是COM中的智能指標,因為在這篇文章中,我們將使用匯入命令將COM介面轉換為智能指標。另外,讀者還需要安裝了微軟的SOAP工具包。
SOAP編程基礎
我們將以介紹一個與基本的SOAP應用程式有關的類開始我們的SOAP編程之旅。然而,我們還必須首先匯入必需的類型庫,我們的應用程式才能使用SOAP類。
匯入類型庫
SOAP中使用的所有對象和介面都包含在mssoap1.dll中,這個檔案包含在Microsoft SOAP Toolkit 2.0中。我們可以在C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll中發現該檔案。使用#import命令就可以將該檔案匯入到我們的源檔案中。類庫檔案中的內容將被轉換為描述了COM介面的COM智能指標。
SOAP使用XML作為其資料格式,因此我們還需要微軟的XML Parser來處理XML內容,它包含在msxml3.dll中。在匯入mssoap1.dll檔案之前,我們還需要匯入該檔案,如下所示:
#import "msxml3.dll" using namespace MSXML2; #import "C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll" \ exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \ "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME") using namespace MSSOAPLib; |
上面是開發一個SOAP應用程式所必需包含的所有類定義。開發一個SOAP用戶端應用程式需要三個步驟:
·指定並串連一個互連網服務。
·準備並發送訊息。
·讀取來自伺服器的響應。
下面是我們用來開發一個基本的SOAP用戶端應用程式所需要用到的類:
SoapConnector
在客戶機/伺服器模式中任何用戶端應用程式需要作的第一件事就是與伺服器進行串連。SoapConnector就是被用來實現客戶機端、伺服器端應用程式連接器的協議,它還充當定義實現其他協議介面的抽象類別,也就是說,SOAP不僅僅局限於充當一種特定的協議。我們會發現,它的一些實現還支援MSMQ、MQ Series、SMTP和TCP/IPTransports。為了簡單起見,我在這裡只討論它作為HTTP Transport的用途,這是由微軟SOAP Toolkit 2.0中的HttpConnector類實現的。
使用SoapConnector類所需要的步驟
首先,建立SoapConnector類的一個對象:
ISoapConnectorPtr connector; Connector.CreateInstance(__uuidof(HttpConnector)); |
然後,指定Web服務的地址。接下來,我們必須詳細描述該Web服務。Web服務是由Property(HttpConnector的一個屬性)指定的。在處理這一屬性時有件事情需要指定:我們引用的哪個屬性以及該屬性的值。下面,我們使用EndPointURL屬性指定Web服務:
Connector->Property ["EndPointURL"] = "some url pointing to web service"; |
下面的表格提供了一個屬性清單(屬性的名字是大小寫敏感的)
屬性 |
描述 |
AuthPassword |
端點認證用的口令。 |
AuthUser |
端點認證用的使用者名稱。 |
EndPointURL |
端點的URL。 |
ProxyPassword |
代理認證的口令。 |
ProxyPort |
Proxy 伺服器使用的連接埠。 |
ProxyServer |
Proxy 伺服器的主機名稱或IP地址。 |
ProxyUser |
代理認證的使用者名稱。 |
SoapAction |
HTTP頭部中SoapAction中的值。這一屬性只能從低級的API中設定,如果使用SoapClient介面中的ConnectorProperty屬性(進階API)設定該屬性,它就會被忽略。 |
SSLClientCertificateName |
如果存在,則該字串標明用於SSL協議中的用戶端認證。其文法為: SSLClientCertificateName [CURRENT_USER | LOCAL_MACHINE\[store-name\]]認證名,其預設的名字為 CURRENT_USER\MY。 |
Timeout |
HttpConnector的逾時時間,這一時間是以毫秒計算的。 |
UseProxy |
一個類型為布爾型的屬性,表明是否使用Proxy 伺服器。預設情況下,這一屬性的值被設定為False,表明無需使用Proxy 伺服器。如果要使用代伺服器,需要將該屬性的值設定為True。如果將該屬性的值設定為True, 而又沒有設定ProxyServer屬性,HttpConnector將使用IE中設定的代理服器。HttpConnector會忽略IE中的“不使用Proxy 伺服器”設定。 |
UseSSL |
表明是否使用了SSL的布爾型值。如果該屬性被設定為True,則無論WSDL中是否指定了HTTP或HTTPS,HttpConnector對象都使用SSL串連。 如果該屬性的值被設定為False,則只有在WSDL中指定了HTTPS的情況下, HttpConnector對象才會使用SSL串連。 |
其次,我們需要與Web服務串連。HttpConnector類的Connect方法用來初始化SoapConnector對象和準備與Web服務的串連。
Connector->Connect();
在與伺服器串連後,我們需要指定Web服務完成的操作。為了指定該操作,我們需要再次使用SoapConnector的Property屬性:
Connector->Property ["SoapAction"] = "some uri"; |
在完成與Web服務的串連和其他的細節後,我們就可以調用向伺服器發送SOAP資訊的方法了,必須在調用SoapSerializer的其他方法之前調用該方法:
Connector->BeginMessage(); |
在完成與資訊相關的操作後,我們必須調用EndMessage()函數,將訊息真正地發給Web服務。
. . [ 訊息準備代碼 ] . . Connector->EndMessage(); |
上面的步驟就是完成與Web服務的實際串連所必需的操作。在下面的部分,我們將討論如何建立和準備一個資訊。
SoapSerializer
SoapSerializer對象用來構建一個向Web服務發送的SOAP訊息。在與伺服器串連前,SoapSerializer對象必須與SoapConnector對象串連。為了使這二個對象相互串連,我們需要調用SoapSerializer對象的Init方法,該方法需要一個參數InputStream(向伺服器發送資料的流):
// 建立一個SoapSerializer對象,並使用InputSTream對它進行初始化 ISoapSerializerPtr Serializer; Serializer.CreateInstance(_uuidof(SoapSerializer)); Serializer->Init(_variant_t((IUnknown*)Connector->InputStream)); |
在討論SoapSerializer的其他函數以前,我們來看一個SOAP請求的例子:
<SOAP: Envelope xmlns:SOAP="soap namespace"> <SOAP:Body> <m:someMethodName xmlns:m="some namespace"> <someParameter> someParameterValue </someParameter> <m:someMethodName> </SOAP:Body> </SOAP: Envelope> |
SOAP請求被封裝在了標記中。<Envelope>標記是該SOAP文檔的主標記,SOAP訊息通常都被封裝在<Envelope>元素中,<Envelope>元素包含一個由<Body>標記指定的訊息體,該訊息體包含著實際的請求。在C++中,有非常合適的方法可以建立這些標記並指定其值。下面的代碼說明了如何利用這些方法:
Serializer->startEnvelope("SOAP","",""); // 開始SOAP訊息中的一個元素,第一個參數描述了名字空間, // 如果它是空值,就會預設地使用SOAP-ENV。第二、第三個參數 // 分別描述了URI和編碼類別型。 Serialzier->startBody(""); // 訊息中<Body>元素的開始,第一個參數描述了編碼風格Uri,其預設的值為NONE。 Serializer->startElement("someMethodName","","","m"); // SOAP訊息中<Body>元素的子項目的開始。第一個參數是子項目名字 //第二個參數是URI,第三個參數是編碼類別型,最後一個參數是元素的名字空間。 Serializer->WriteString("someParameterValue") // 寫元素的值 |
上面以startXXX開頭的函數都相應地有以endXXX開頭、結束元素的函數。在完成訊息後,系統會調用串連的endMessage()方法,真正開始向服務發送訊息。
現在我們已經與服務相串連,準備好了我們的請求,並將它發送給了服務。最後一個步驟就是讀取來自伺服器的響應。下面我們就來討論這一問題。
SoapReader
該對象讀取來自Web服務的響應,並將它解析為DOM,以備進一步處理之用。下面是一個來自Web服務的響應的例子:
<SOAP: Envelope xmlns:SOAP="soap namespace"> <SOAP:Body> <m:someMethodNameResponse xmlns:m="some namespace"> <return> someResult </return> <m:someMethodNameResponse> </SOAP:Body> </SOAP: Envelope> |
在調用任何方法擷取結果前,我們聯結OutputStream,讀取儲存在SoapReader對象中的響應(OutputStream用於接收來自Web服務的資料):
// 建立SOAPReader對象和與outputstream聯結的代碼 ISoapReaderPtr Reader; Reader.CreateInstance(_uuidof(SoapReader)); Reader->Load(_variant_t((IUnknown*)Connector->OutputStream)); // load方法也能夠接收XML文檔檔案或字串 |
在將Web服務的響應載入到SoapReader對象後,我們可以通過調用SoapReader對象的RPCResult屬性獲得相應的結果,但RPCResult並不返回真正的結果,它返回的是<Body>元素中第一個條目的第一個子項目。我們可以通過調用text屬性返回真正的結果:
一個SOAP用戶端應用程式的例子
為了說明如何使用本篇文章中討論的SOAP類,我們使用了http://www.xmethods.net/上列出的一項服務,該服務能夠顯示使用者是否正在使用Yahoo Messenger。它只需要一個參數,即Yahoo使用者的登入ID。返回的結果是一個布爾型值,0表示使用者不線上,1表示使用者線上。
我一直認為,學習某種編程技術的最好的方法就是實地學習原始碼,在這裡,我們就採取這種方法。下面是使用SOAP調用發現Yahoo使用者是否線上的一個控制台應用程式的C++代碼:
#include #import "msxml3.dll" using namespace MSXML2; #import "C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll" \ exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", \ "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME") using namespace MSSOAPLib; void main() { CoInitialize(NULL); ISoapSerializerPtr Serializer; ISoapReaderPtr Reader; ISoapConnectorPtr Connector; // 與Web服務串連 Connector.CreateInstance(__uuidof(HttpConnector)); Connector->Property["EndPointURL"] = "http://www.allesta.net:51110/webservices/soapx4/isuseronline.php"; Connector->Connect(); // 開始訊息 Connector->Property["SoapAction"] = "uri:allesta-YahooUserPing"; Connector->BeginMessage(); // 建立SoapSerializer對象 Serializer.CreateInstance(__uuidof(SoapSerializer)); // 將serializer串連到connector的輸入字串 Serializer->Init(_variant_t((IUnknown*)Connector->InputStream)); // 建立SOAP訊息 Serializer->startEnvelope("","",""); Serializer->startBody(""); Serializer->startElement("isuseronline","uri:allesta-YahooUserPing","","m"); Serializer->startElement("username","","",""); Serializer->writeString("laghari78"); Serializer->endElement(); Serializer->endElement(); Serializer->endBody(); Serializer->endEnvelope(); // 將該訊息發送給web服務 Connector->EndMessage(); // 讀取響應 Reader.CreateInstance(__uuidof(SoapReader)); // 將reader聯結到connector的輸出字串 Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), ""); // 顯示結果 printf("Answer: %s\n", (const char *)Reader->RPCResult->text); CoUninitialize(); } |
我們可以看到,代碼十分簡單,即使沒有使用過C++,我保證讀者也能夠理解上面代碼的作用:首先,它與遠程伺服器串連;其次,它建立SOAP訊息並向web服務發送該訊息;最後,讀取伺服器的響應,並使用printf將它輸出到螢幕上。
結論
在本篇文章中,我們討論了如何使用Visual C++建立一個簡單的SOAP用戶端應用程式。我們還學習了SOAP Toolkit中的幾個方法以及如何使用SOAP從伺服器擷取資料。希望通過本篇文章,使讀者能夠掌握如何使用C++開發SOAP用戶端應用程式。