整合分布式應用程序經常是一件非常困難並且錯綜復雜的任務即使是最富有經驗的開發者也可能會覺得頭疼當應用程序在不同的操作系統以及涉及不同的程序平台時這個集成問題變得尤其復雜雖然說Web服務承諾可以減輕程序員完成集成任務的困難程度但是也可能給程序員們帶來一些意想不到的麻煩在這裡我們將把一個應用程序和一個PHP Web服務連結起來以學習一些整合分布式應用程序的方法以及必要的應對措施包括運行什麼以及不用去做什麼
這個Web服務在一個Apache服務器上運行並且使用PHP開發它從各種微軟新聞組檢索新聞摘要以及它們的關聯的文本即使由這個服務提供的數據可以直接使用內部的Net對象存取但是這個服務還是將使用並提供一個連接到非Net平台上的不錯的演示我們這裡要討論的實例基於Net beta 版
創建一個Web服務代理
Visual StudioNET提供了一個出色的機制用於自動地生成可用於存取遠程Web服務的代理對像因此要首先嘗試使用這些函數來導入由PHP服務提供的Web服務描述語言(Web Services Description LanguageWSDL)文件 還可以使用Net SDK的WSDLexe命令行公用程序不幸的是在使用向導導入WSDL之後並不能成功地創建一個代理所以我必須把導入原始的WSDL文件後由VSNet生成的文件轉換為WSDL
◆把模式域名空間從改成 然後清除所有的當WSDL導入過程中由VSNet添加的q域名空間
◆刪除 xmlns:tm=和xmlns: mime= 名字空間因為這個應用程序中不需要包含這些
◆刪除類型元素因為原始的 WSDL文檔 並沒有包含Web服務的模式信息的指定的元素區段
◆改變輸入輸出元素消息屬性值為包含tns域名空間前綴的形式:
<portType name=nntpSoapPortType>
<operation name=getheaders
parameterOrder=newsgroup numitems>
<input message=tns:getheaders />
<output message=tns:getheadersresponse />
</operation>
<operation name=getarticle
parameterOrder=newsgroup article>
<input message=tns:getarticle />
<output message=tns:getarticleresponse />
</operation>
</portType>
在進行了下面的這些微小的改變VSNet向導能夠讀取WSDL並且自動地生成一個代理在編譯了這個代理之後它被包含在一個ASPNET頁面中然而當這個ASPNet頁面被執行 message does not have a correct SOAP root XML tag這個錯誤被當作一個SOAP錯誤從Web服務中返回
為了精確地評估這個錯誤代理調用被一個名為Proxy Trace的公用程序使用以便代理生成SOAP包裝這可以通過把下列代碼添加進ASPNet頁面來實現
msNewsProxy = new SystemNetWebProxy(//localhost:);
在察看了由Net代理生成的SOAP包裝之後我有點奇怪為什麼會返回這個錯誤因為實際上一個相對的SOAP包裝被生成並被發送到Web服務即使在嘗試了好幾個轉化成代理代碼之後這個錯誤依然持續代碼段列表顯示了從PHP Web服務返回的完整的SOAP錯誤包裝
在使用VSNet中創建的代理對象的好幾個把ASPNet頁面與PHP Web服務連結的不成功的嘗試之後我決定從頭開始創建SOAP包裝以便執行更有效的程序調試{起先它看起來好像由Net代理生成的模式域名空間可能是問題的關鍵因為Net使用模式規范而PHP服務使用的是版本的規范
然而我把自定義的SOAP包裝改為用版本代替版本錯誤依然存在在嘗試了好幾個其他的小的改變之後我決定把SOAP包裝使用的域名空間前綴和正文元素從soap (由Net代理生成)改為SOAP ENV因為我看見在SOAP錯誤信息中返回了SOAP ENV前綴(見代碼)這表面上看上去微不足道的改變竟解決了問題!當處理任何請求的時候PHP服務顯然需要SOAP ENV前綴而拒絕不包含SOAP ENV前綴的要求
創建一個自定義代理
既然已經了解了為什麼Web服務返回一個SOAP錯誤我們就可以創建一個自定義代理來生成網服務期待的SOAP包裝雖然創建一個自定義SOAP包裝肯定比使用一個由或者WSDLexe公用程序生成的SOAP包裝要花更多的時間但是這樣做可以完全控制包裝的內容為了開始創建自定義代理我創建一個名為msnewsserviceproxy的包含兩個字段的新類
public class MSNewsServiceProxy {
string _uri;
string _soapAction;
}
uri字段保存了Web服務的位置而_soapAction字段保存了將要使用SOAP包裝發送的SOAPAction數據頭的名稱在MSNewsServiceProxy類之內添加CreateSoapEnvelope ()SendSoapEnvelope ()和FilterResult ()這三個方法這些方法生成SOAP包裝請求把它發送到Web服務然後過濾返回的SOAP包裝讓我們逐一的看看每個方法注意代碼在SOAP包裝的根元素上添加一個SOAP ENV域名空間前綴Web服務顯然需要這個特定的前綴而拒絕任何不包含這個前綴的信息因為生成的代理發送一個soap域名空間前綴(而不是SOAP ENV)所以它的消息被拒絕Web服務不應該需要一個特定的域名空間前綴而為此拒絕不帶此前綴的消息但是域名空間問題也是你必須注意要想使工作更好的完成要執行一些看上去不{>可思議的事情
在SOAP包裝被創建之後SendSoapEnvelope ()方法(見代碼段)使用了幾個SystemNet和SystemIO域名空間中的類來把這個包裝發送到Web服務中代碼首先通過把_uri變量傳送到對象構造器來創建一個HttpWebRequest對象其次與這個請求相關聯的相應的MethodContentType和Header都將被發送
然後一個StreamWriter對象和HttpWebRequest對象的請求流相關聯SOAP包裝就被使用StreamWriter的Write ()方法寫到流中
從Web服務返回的SOAP包裝被HttpWebResponse對象的SendSoapEnvelope ()方法獲得
HttpWebResponse response = (HttpWebResponse)requestGetResponse();
如果應答不是空值它將被載入一個XMLTextReaderXMLTextReader被用來填充XmlDocument對象然後從這個方法中返回XmlDocument對象
FilterSoapEnvelope ()方法分析SOAP應答包裝並把從Web服務中返回的數據裝入自定義代理的消費者使用的XmlDocument對象
private XmlDocument
FilterSoapEnvelope(
XmlDocument doc) {
XmlDocument filterDoc =new XmlDocument();
XmlNode result = docSelectSingleNode(//results);
XmlNode resultImport = filterDocImportNode(resulttrue);
filterDocAppendChild(resultImport);
return filterDoc;
}
雖然過濾器可以使用好幾種方法執行但是FilterSoapEnvelope ()方法依靠XPath語句可以在應答SOAP包裝中得到結果元素
微軟新聞組PHP Web服務展示了允許取得新聞組新聞摘要的兩種方法getheaders ()和getmessage () 你可以看到如何在自定義代理類中使用這兩種方法(見代碼段) 注意每個方法中的代碼傳遞Web服務方法名被調用到CreateSoapEnvelope ()方法和任何使用這個方法關聯的參數 在SOAP包裝被發送以及應答被接受之後FilterSoapEnvelope ()方法被調用來把返回的數據加載到一個XmlDocument對象中同樣這個對象也是代理消費者使用的
From:http://tw.wingwit.com/Article/program/net/201311/12274.html