<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> |
在進行了下面的這些微小的改變,VS.Net向導能夠讀取WSDL并且自動地生成一個代理。在編譯了這個代理之后,它被包含在一個ASP.Net頁面中。然而,當這個ASP.Net頁面被執行:“ message does not have a correct SOAP root XML tag.”,這個錯誤被當作一個SOAP錯誤從Web服務中返回。為了精確地評估這個錯誤,代理調用被一個名為Proxy Trace的公用程序使用,以便代理生成SOAP包裝。這可以通過把下列代碼添加進ASP.Net頁面來實現:
msNews.Proxy = new System.Net.WebProxy( "http://localhost:8080");
在察看了由.Net代理生成的SOAP包裝之后,我有點奇怪為什么會返回這個錯誤,因為實際上一個相對的SOAP包裝被生成并被發送到Web服務。即使在嘗試了好幾個轉化成代理代碼之后這個錯誤依然持續。代碼段列表2顯示了從PHP Web服務返回的完整的SOAP錯誤包裝。
在使用VS.Net中創建的代理對象的好幾個把ASP.Net頁面與PHP Web服務連結的不成功的嘗試之后,我決定從頭開始創建SOAP包裝以便執行更有效的程序調試。{起先,它看起來好像由.Net代理生成的模式域名空間可能是問題的關鍵,因為.Net使用2001模式規范而PHP服務使用的是1999版本的規范。
然而,我把自定義的SOAP包裝改為用1999版本代替2001版本,錯誤依然存在。在嘗試了好幾個其他的小的改變之后,我決定把SOAP包裝使用的域名空間前綴和正文元素從soap (由.Net代理生成)改為SOAP - ENV,因為我看見在SOAP錯誤信息中返回了SOAP - ENV前綴。(見代碼2)這表面上看上去微不足道的改變竟解決了問題!當處理任何請求的時候,PHP服務顯然需要SOAP - ENV前綴,而拒絕不包含SOAP - ENV前綴的要求。
創建一個自定義代理
既然已經了解了為什么Web服務返回一個SOAP錯誤,我們就可以創建一個自定義代理來生成網服務期待的SOAP包裝。雖然創建一個自定義SOAP包裝肯定比使用一個由VS.Net或者WSDL.exe公用程序生成的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服務顯然需要這個特定的前綴,而拒絕任何不包含這個前綴的信息。因為VS.Net生成的代理發送一個soap域名空間前綴(而不是SOAP - ENV),所以它的消息被拒絕。Web服務不應該需要一個特定的域名空間前綴而為此拒絕不帶此前綴的消息,但是域名空間問題也是你必須注意要想使工作更好的完成,要執行一些看上去不{0>可思議的事情。
在SOAP包裝被創建之后,SendSoapEnvelope ()方法(見代碼段4)使用了幾個System.Net和System.IO域名空間中的類來把這個包裝發送到Web服務中。代碼首先通過把_uri變量傳送到對象構造器來創建一個HttpWebRequest對象。其次,與這個請求相關聯的相應的Method,ContentType和Header都將被發送。然后一個StreamWriter對象和HttpWebRequest對象的請求流相關聯,SOAP包裝就被使用StreamWriter的Write ()方法寫到流中。
從Web服務返回的SOAP包裝被HttpWebResponse對象的SendSoapEnvelope ()方法獲得。
HttpWebResponse response = (HttpWebResponse)request.GetResponse(); |
如果應答不是空值,它將被載入一個XmlTextReader,XmlTextReader被用來填充XmlDocument對象。然后從這個方法中返回XmlDocument對象。
FilterSoapEnvelope ()方法分析SOAP應答包裝并把從Web服務中返回的數據裝入自定義代理的“消費者”使用的XmlDocument對象:
private XmlDocument FilterSoapEnvelope( XmlDocument doc) { XmlDocument filterDoc =new XmlDocument(); XmlNode result = doc.SelectSingleNode("http://results"); XmlNode resultImport = filterDoc.ImportNode(result,true); filterDoc.AppendChild(resultImport); return filterDoc; } |
雖然過濾器可以使用好幾種方法執行,但是FilterSoapEnvelope ()方法依靠XPath語句可以在應答SOAP包裝中得到結果元素。
微軟新聞組PHP Web服務展示了允許取得新聞組新聞摘要的兩種方法:getheaders ()和getmessage ()。 你可以看到如何在自定義代理類中使用這兩種方法(見代碼段5)。 注意每個方法中的代碼傳遞Web服務方法名被調用到CreateSoapEnvelope ()方法和任何使用這個方法關聯的參數。 在SOAP包裝被發送以及應答被接受之后,FilterSoapEnvelope ()方法被調用來把返回的數據加載到一個XmlDocument對象中,同樣,這個對象也是代理“消費者”使用的。
整合PHP Web服務
既然一個自定義代理類已經準備好被使用,那么把它整合進一個ASP.Net頁面就變得很簡單了。getHeaders ()和getMessage ()方法可以調用Web服務,存取返回的XmlDocument對象(見代碼段6和7) 在XmlDocument內的子結點中的枚舉可以顯示這些數據。 雖然許多Web服務可以很容易地使用VS.Net或者WSDL.exe創建的代理類自動地生成,但是仍然會有你需要創建自定義SOAP包裝來把.Net和其他的平臺整合起來的情況。 本文中介紹的內容以及代碼就提供了完成這個整合工作的一種方法。
代碼段1:
POST http://www.codecraze.com/soap/nntp.php HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.0.2914.16) Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.codecraze.com/soap/nntp.php" Content-Length: 683 Expect: 100-continue Connection: Keep-Alive Host: www.codecraze.com <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap= "http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/ encoding/" xmlns:tns="http://tempuri.org/" xmlns:types="http://tempuri.org/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body soap:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/"> <q1:getheaders xmlns:q1= "http://www.codecraze.com/soap/nntp.xsd"> <newsgroup xsi:type="xsd:string"> microsoft.public.dotnet.xml </newsgroup> <numitems xsi:type="xsd:string">15</numitems> </q1:getheaders> </soap:Body> </soap:Envelope> |
代碼段2:
HTTP/1.1 200 OK Date: Apr, 16 Dec 2002 15:57:47 GMT Server: Apache/1.3.20 (Unix) ApacheJServ/1.1.2 PHP/ 4.0.4pl1 FrontPage/5.0.2.2510 Rewrit/1.1a X-Powered-By: PHP/4.0.4pl1 Content-Length: 419 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/xml; charset="utf-8" <?xml version="1.0" encoding="utf-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/ envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/ soap/encoding/"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Client.SOAPMessageFormat</ faultcode> <faultstring> message does not have a correct SOAP root XML tag </faultstring> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
代碼段3 :
private string CreateSoapEnvelope( string method,string[] param1, string[] param2) { StringBuilder sb = new StringBuilder(); sb.Append("<SOAP-ENV:Envelope"); sb.Append(" xmlns:SOAP-" + "ENV=\"http://schemas.xmlsoap.org/soap/ envelope/\""); sb.Append(" xmlns:xsi= \"http://www.w3.org/1999/XMLSchema-" + instance\""); sb.Append(" xmlns:xsd= \"http://www.w3.org/1999/XMLSchema\">"); sb.Append("<SOAP-ENV:Body>"); sb.Append("<m:" + method + " xmlns:m=\"nntp.xsd\">"); sb.Append("<" + param1[0] + ">" + param1[1] + "</" + param1[0] + ">"); sb.Append("<" + param2[0] + ">" + param2[1] + "</" + param2[0] + ">"); sb.Append("</m:" + method + ">"); sb.Append("</SOAP-ENV:Body>"); sb.Append("</SOAP-ENV:Envelope>"); return sb.ToString(); } |
代碼段4:
private XmlDocument SendSoapEnvelope(string soapEnv) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_uri); XmlTextReader xmlReader = null; XmlDocument xmlDoc = null; request.Method = "POST"; request.ContentType = "text/xml"; request.Headers.Add("SOAPAction",_soapAction); StreamWriter writer = new StreamWriter( request.GetRequestStream()); writer.Write(soapEnv); writer.Close(); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (response != null) { xmlReader = new XmlTextReader( response.GetResponseStream()); xmlDoc = new XmlDocument(); xmlDoc.Load(xmlReader); return xmlDoc; } else { return xmlDoc; } } |
代碼段5:
public XmlDocument getHeaders(string newsgroup, string numitems) { string soapEnv = CreateSoapEnvelope("getheaders", new string[2]{"newsgroup",newsgroup}, new string[2]{"numitems",numitems}); return FilterSoapEnvelope(SendSoapEnvelope(soapEnv)); } public XmlDocument getMessage(string newsgroup, string article) { string soapEnv = CreateSoapEnvelope("getmessage", new string[2]{"newsgroup",newsgroup}, new string[2]{"article",article}); return FilterSoapEnvelope(SendSoapEnvelope(soapEnv)); } |
代碼段6:
private void Page_Load(object sender, System.EventArgs e) { StringBuilder sb = new StringBuilder(); try { MSNewsService.MSNewsServiceProxy news = new MSNewsService.MSNewsServiceProxy(); XmlDocument newsHeaders = news.getHeaders("microsoft.public.dotnet.xml", "15"); sb.Append("<ul>"); foreach (XmlNode node in newsHeaders.DocumentElement.ChildNodes) { sb.Append("<li><a href=\"JavaScript:readArticle( '" + node.FirstChild.InnerText + "','" + node.ChildNodes.Item(1).InnerText + "')\">" + node.ChildNodes.Item(1).InnerText + "</a></li>"); } sb.Append("<ul>"); lblMessages.Text = sb.ToString(); } catch (Exception exp) { lblMessages.Text = "The Web Service appears to be" + " unavailable"; } } |
代碼MSNewsServiceProxy.cs:
using System.Net; using System.Xml; using System.IO; using System.Text; namespace MSNewsService { public class MSNewsServiceProxy { string _uri; string _soapAction; public MSNewsServiceProxy() { _uri = "http://www.codecraze.com/soap/nntp.php"; _soapAction = "http://www.codecraze.com/soap/nntp.php"; } private XmlDocument SendSoapEnvelope(string soapEnv) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_uri); XmlTextReader xmlReader = null; XmlDocument xmlDoc = null; request.Method = "POST"; request.ContentType = "text/xml"; request.Headers.Add("SOAPAction",_soapAction); StreamWriter writer = new StreamWriter(request.GetRequestStream()); writer.Write(soapEnv); writer.Close(); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (response != null) { xmlReader = new XmlTextReader(response.GetResponseStream()); xmlDoc = new XmlDocument(); xmlDoc.Load(xmlReader); return xmlDoc; } else { return xmlDoc; } } private string CreateSoapEnvelope(string method,string[] param1, string[] param2) { StringBuilder sb = new StringBuilder(); sb.Append("<SOAP-ENV:Envelope"); sb.Append(" xmlns:SOAP-" + "ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""); sb.Append(" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-" + "instance\""); sb.Append(" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">"); sb.Append("<SOAP-ENV:Body>"); sb.Append("<m:" + method + " xmlns:m=\"nntp.xsd\">"); sb.Append("<" + param1[0] + ">" + param1[1] + "</" + param1[0] + ">"); sb.Append("<" + param2[0] + ">" + param2[1] + "</" + param2[0] + ">"); sb.Append("</m:" + method + ">"); sb.Append("</SOAP-ENV:Body>"); sb.Append("</SOAP-ENV:Envelope>"); return sb.ToString(); } public XmlDocument getHeaders(string newsgroup,string numitems) { string soapEnv = CreateSoapEnvelope("getheaders", new string[2]{"newsgroup",newsgroup}, new string[2]{"numitems",numitems}); return FilterSoapEnvelope(SendSoapEnvelope(soapEnv)); } public XmlDocument getMessage(string newsgroup,string article) { string soapEnv = CreateSoapEnvelope("getmessage", new string[2]{"newsgroup",newsgroup}, new string[2]{"article",article}); return FilterSoapEnvelope(SendSoapEnvelope(soapEnv)); } private XmlDocument FilterSoapEnvelope(XmlDocument doc) { XmlDocument filterDoc = new XmlDocument(); XmlNode result = doc.SelectSingleNode("http://results"); XmlNode resultImport = filterDoc.ImportNode(result,true); filterDoc.AppendChild(resultImport); return filterDoc; } } } |
代碼msNewsClient.aspx.cs
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Xml; using System.Text; namespace MSNewsService { public class MSNewsServiceClient : System.Web.UI.Page { protected System.Web.UI.WebControls.Label lblMessages; public MSNewsServiceClient() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, System.EventArgs e) { StringBuilder sb = new StringBuilder(); try { MSNewsService.MSNewsServiceProxy news = new MSNewsService.MSNewsServiceProxy(); XmlDocument newsHeaders = news.getHeaders("microsoft.public.dotnet.xml","15"); sb.Append("<ul>"); foreach (XmlNode node in newsHeaders.DocumentElement.ChildNodes) { sb.Append("<li><a href=\"JavaScript:readArticle('" + node.FirstChild.InnerText + "','" + node.ChildNodes.Item(1).InnerText + "')\">" + node.ChildNodes.Item(1).InnerText + "</a></li>"); } sb.Append("<ul>"); lblMessages.Text = sb.ToString(); } catch (Exception exp) { lblMessages.Text = "The Web Service appears to be unavailable"; } } private void Page_Init(object sender, EventArgs e) { InitializeComponent(); } private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } } } |
代碼msNewsMessage.aspx
<%@ Page language="c#" src="msNewsMessage.aspx.cs" Codebehind="msNewsMessage.aspx.cs" AutoEventWireup="false" Inherits="MSNewsService.msNewsMessage" %> <HTML> <body bgcolor="#ffffff"> <h2> <asp:Label ID="lblMessageTitle" Runat="server" /> </h2> <p> <asp:Label ID="lblMessageText" Runat="server" /> </p> </body> </HTML> 代碼msNewsMessage.aspx.cs using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Xml; namespace MSNewsService { public class msNewsMessage : System.Web.UI.Page { protected System.Web.UI.WebControls.Label lblMessageTitle; protected System.Web.UI.WebControls.Label lblMessageText; public msNewsMessage() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, System.EventArgs e) { try { MSNewsService.MSNewsServiceProxy news = new MSNewsService.MSNewsServiceProxy(); XmlDocument newsHeaders = news.getMessage("microsoft.public.dotnet.xml", Request["articleID"]); lblMessageTitle.Text = Request["title"]; lblMessageText.Text = newsHeaders.FirstChild.InnerText; } catch (Exception exp) { lblMessageText.Text = "The Web Service appears to be unavailable"; } } private void Page_Init(object sender, EventArgs e) { InitializeComponent(); } private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } } } |
代碼msNewsClient.apsx
<%@ Page language="c#" src="msNewsClient.aspx.cs" Codebehind="msNewsClient.aspx.cs" AutoEventWireup="false" Inherits="MSNewsService.MSNewsServiceClient" %> <HTML> <head> <script language="javascript"> function readArticle(id,title) { openWindow("msNewsMessage.aspx?articleID=" + id + "&title=" + title,500,400); } function openWindow(page,w,h) { window.open(page,"","status=no,width=" + w + ",height=" + h + ",scrollbars=yes"); } </script> </head> <body bgcolor="#ffffff"> <h2>Recent posts from: microsoft.public.dotnet.xml</h2> <p /> <asp:Label ID="lblMessages" Runat="server" /> </body> </HTML><0} |
原文轉自:http://www.anti-gravitydesign.com