|
Soap是一個(gè)在信息交換中使用得非常廣泛的協(xié)議,使用方便,并直接可與HTTP, SMTP等其它協(xié)議一起工作。本文討論如何使用Microsoft SOAP Tookit的C++來建立一個(gè)簡(jiǎn)單的SOAP客戶端應(yīng)用。 ========================================================= 一、先決條件: 必須熟悉使用COM,特別要熟悉COM中的Smart Pointers。我通過導(dǎo)入方法將COM接口轉(zhuǎn)換成Smart Pointers。系統(tǒng)必須安裝了Microsoft SOAP Toolkit和Microsoft XML Parser。文末參考一節(jié)介紹如何下載工具箱。文末附件可下載本文源程序。
二、SOAP編程基礎(chǔ): 下面開始介紹一個(gè)簡(jiǎn)單SOAP應(yīng)用中所包含的類。在此之前,必需先導(dǎo)入所需的類型庫,然后程序才能夠使用SOAP的類。
導(dǎo)入類型庫: SOAP中使用的對(duì)象和接口都在mssoap1.dll文件中。這個(gè)文件在安裝Microsoft SOAP Toolkit 2.0時(shí)生成,存在路徑:"C:\Program Files\Common Files\MSSoap\Binaries\MSSOAP1.dll"。用#import將該文件導(dǎo)入到程序中。類型庫的內(nèi)容在導(dǎo)入時(shí)被轉(zhuǎn)換成COM smart pointers來描述COM接口。因?yàn)镾OAP完全依賴于XML,因此必需用Microsoft XML Parser來處理XML。Microsoft XML parser在msxml3.dll文件里。這個(gè)文件要在導(dǎo)入mssoap1.dll之前導(dǎo)入。
#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客戶端應(yīng)用有以下三步驟: 1- 指定和連接Web服務(wù)器。 2- 準(zhǔn)備和發(fā)送消息。 3- 讀取服務(wù)端返回的信息。
下面是在基本SOAP客戶端要使用到的類:
1- SoapConnector: 在客戶/服務(wù)模式下,首先要做的事就是連接服務(wù)器。SoapConnector類執(zhí)行客戶端與服務(wù)端之間的消息傳送協(xié)議。 SoapConnector是一個(gè)抽象類,定義了協(xié)議執(zhí)行的接口。事實(shí)上, SoapConnector類不定義執(zhí)行某種特定的傳送協(xié)議,例如:MSMQ, MQ Series, SMTP 和 TCP/IP等。 為簡(jiǎn)便起見,本文只說明使用HTTP傳送協(xié)議,它是由Microsoft SOAP Toolkit 2.0中的HttpConnector 類來執(zhí)行的。
SoapConnector類使用步驟如下: a) 創(chuàng)建SoapConnector類對(duì)象: ISoapConnectorPtr connector; Connector.CreateInstance(__uuidof(HttpConnector));
b) 指定Web服務(wù)器地址: 指定服務(wù)器,要做二件事:選擇HttpConnector的屬性和相應(yīng)的屬性值。本文示例選用EndPointURL屬性: Connector->Property ["EndPointURL"] = "some url pointing to web service";
以下是屬性選項(xiàng)說明(屬性名是大小寫敏感的): AuthPassword:客戶口令 AuthUser:客戶名 EndPointURL :客戶URL ProxyPassword: 代理(proxy)口令 ProxyPort :代理斷口 ProxyServer :代理服務(wù)器的IP地址或主機(jī)名 ProxyUser :代理用戶名 SoapAction:HTTP的抬頭值。這個(gè)屬性只使用于低級(jí)API。它將忽略SoapClient接口(高級(jí)API)中的ConnectorProperty屬性 。 SSLClientCertificateName:指定使用Secure Sockets Layer (SSL)加密協(xié)議。語法如下: [CURRENT_USER | LOCAL_MACHINE\[store-name\]]cert-name with the defaults being CURRENT_USER\MY (與Microsoft Internet Explorer用法相同)。 Timeout:HttpConnector的超時(shí)限制,以毫秒為單位。 UseProxy:定義是否使用代理(proxy)。缺省值為False。如果將這個(gè)屬性為真(True),又沒有設(shè)置上面的ProxyServer值,代理服務(wù)器將使用IE里的代理服務(wù)器。此時(shí)HttpConnector將不理會(huì)IE的"Bypass Proxy"(繞道)設(shè)置。 UseSSL:定義是否使用SSL(True 或 False)。此值設(shè)置為真時(shí),HttpConnector對(duì)象不管WSDL設(shè)置是HTTP或HTTPS都用SSL連接方式。若此值設(shè)置為非真,HttpConnector對(duì)象只在WSDL設(shè)置為HTTPS時(shí)才用SSL方式連接。 c) 與Web服務(wù)器連接: Connector->Connect();
d) 指定動(dòng)作: Connector->Property ["SoapAction"] = "some uri";
e) 啟動(dòng)消息句柄: 必需在SoapSerializer(消息準(zhǔn)備函數(shù))之前先啟動(dòng)消息處理機(jī)制 Connector->BeginMessage();
在消息處理完畢之后,用EndMessage()函數(shù)將消息送往服務(wù)器。 . . [ 消息準(zhǔn)備代碼 ] . . Connector->EndMessage();
以上就是與服務(wù)器連接的過程。下面介紹如何創(chuàng)建和準(zhǔn)備消息。
SoapSerializer: 用于建立送往服務(wù)器的SOAP消息。在與服務(wù)器通訊之前,SoapSerializer對(duì)象必需先與SoapConnector對(duì)象連接。SoapSerializer的初始化函數(shù)將建立這個(gè)內(nèi)部連接。初始化代入的參數(shù)是InputStream (數(shù)據(jù)流): // 創(chuàng)建SoapSerializer對(duì)象,并用InputSTream進(jìn)行初始化。 ISoapSerializerPtr Serializer; Serializer.CreateInstance(_uuidof(SoapSerializer)); Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
下面是SOAP請(qǐng)求代碼:
<SOAP: Envelope xmlns:SOAP="soap namespace"> <SOAP:Body> <m:someMethodName xmlns:m="some namespace"> <someParameter> someParameterValue </someParameter> <m:someMethodName> </SOAP:Body> </SOAP: Envelope>
SOAP請(qǐng)求被安放在標(biāo)記之中。<Envelope>是SOAP文件的主標(biāo)記。SOAP信息通常都安放在”信封“(Envelope)里。信封里的<Body>標(biāo)記中安放信息體,其中包含具體請(qǐng)求。在C++里,用相應(yīng)的方法來解釋這些標(biāo)記并定義有關(guān)的值。 下面的代碼說明如何使用這些方法:
Serializer->startEnvelope("SOAP","",""); // 開始處理SOAP消息。第一個(gè)參數(shù)是命名空間,缺省為SOAP-ENV。 // 第二個(gè)參數(shù)定義URI。第三個(gè)參數(shù)定義Serialzier->startBody("")函數(shù)的編碼方式。 // 開始處理<Body>元素,第一個(gè)參數(shù)是URI的編碼類型,缺省為NONE。
Serializer->startElement("someMethodName","","","m"); // 開始處理Body里的子元素。 // 第一個(gè)參數(shù)是元素名。第二個(gè)參數(shù)是URI。 // 第三個(gè)參數(shù)編碼類型。第四個(gè)參數(shù)是元素的命名空間。
Serializer->WriteString("someParameterValue") // 寫入元素值
在上面的每個(gè)startXXX函數(shù)后都要又相應(yīng)的endXXX函數(shù)來結(jié)尾。消息做完之后,連接器就調(diào)用endMessage()方法將消息發(fā)送到服務(wù)器。
至此,我們已經(jīng)連接了服務(wù)器,制作了相應(yīng)的消息。最后一個(gè)步驟就是接收服務(wù)器回應(yīng)。
SoapReader: 讀取服務(wù)器返回的信息,將信息解析之后裝入DOM,為進(jìn)一步處理所用。下面是服務(wù)器返回的SOAP回應(yīng)信息:
<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對(duì)象中的信息。(OutputStream接收服務(wù)器返回的信息)。
// 創(chuàng)建SOAPReader對(duì)象,并連接到outputstream ISoapReaderPtr Reader; Reader.CreateInstance(_uuidof(SoapReader)); Reader->Load(_variant_t((IUnknown*)Connector->OutputStream)); // load方法還可以用于加載XML文件或字符串
將回應(yīng)信息加載到SoapReader對(duì)象之后,就可以用它的RPCResult屬性來獲取結(jié)果。不過,But RPCResult并不直接返回結(jié)果,它返回<Body>的第一個(gè)實(shí)體元素,然后用text屬性讀取該元素屬性值: Reader->RPCResult->text
三、舉例說明一個(gè)簡(jiǎn)單的SOAP客戶端應(yīng)用: 本文示例用www.xmethods.net做服務(wù)器。這個(gè)服務(wù)器指向Yahoo在線信息。 可以在http://www.xmethods.net/ve2/ViewListing.po?serviceid=156找到有關(guān)細(xì)節(jié)。 下面的代碼中要輸入一個(gè)參數(shù),即Yahoo的用戶ID。返回結(jié)果為0表示離線,1表示在線。 其他細(xì)節(jié)可參閱:http://www.allesta.net:51110/webservices/wsdl/YahooUserPingService.xml
四、參考: The SOAP specification Simple Object Access Protocol (SOAP) 1.1 - W3C Note : http://www.w3.org/TR/SOAP Microsoft SOAP Toolkit Download : http://download.microsoft.com/download/xml/soap/2.0/w98nt42kme/EN-US/SoapToolkit20.exe
五:本文示例的SOAP代碼: #include <stdio.h>
#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;
// 連接服務(wù)器 Connector.CreateInstance(__uuidof(HttpConnector)); Connector->Property["EndPointURL"] = "http://www.allesta.net:51110/webservices/soapx4/isuseronline.php"; Connector->Connect();
// 啟動(dòng)消息機(jī)制 Connector->Property["SoapAction"] = "uri:allesta-YahooUserPing"; Connector->BeginMessage();
// 創(chuàng)建SoapSerializer對(duì)象 Serializer.CreateInstance(__uuidof(SoapSerializer));
// 與輸入流連接 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();
// 向服務(wù)器發(fā)送信息 Connector->EndMessage();
// 讀取回應(yīng) Reader.CreateInstance(__uuidof(SoapReader));
// 連接輸出流 Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), "");
// 顯示結(jié)果 printf("Answer: %s\n", (const char *)Reader->RPCResult->text); CoUninitialize(); }
本文附件
http://www.topxml.com/snippetcentral/snippetfiles/v20020425121357.zip
|