1. 程式人生 > 實用技巧 >.net 呼叫 Java編寫的WebService

.net 呼叫 Java編寫的WebService

經常使用.net訪問第三方系統介面,特別是訪問Java編寫的WebService經常碰到各種各樣的問題,例如:

  1.新增Web引用出錯,使用wsdl生成代理類過程出錯(這個問題困擾我很久,可能是wsdl格式解釋的問題吧,具體未找到原因)

  2.不能新增Web引用(對方給的介面地址,根本得不到WSDL),使用對方提供的wsdl生成的代理類執行出錯。微軟生成的代理類產生的Soap是以"soap"標記,而對方只認"soapenv"標記(在網上看到有人說可以通用,但對方介面就是不認)

  因此我們可能要變通的訪問,下面是在網上找到的比較好的方法

  ================================================================

  如果.NET呼叫WebService,最簡單的辦法當然是直接新增WEB引用,然後自動產生代理類,但是在呼叫JAVA的WebService時並沒有這麼簡單,特別是對於SoapHeader的處理。
  先說說的思路:
  1、先用soapUI進行測試,這個工具會自動生成呼叫某個方法的XML。
  2、把soapUI生成的XML作為模版,自己也生成一個一模一樣的XML併為引數節點賦好值。
  3、將這個XML通過http直接傳送給WebService。
  4、接收返回的XML進行處理。
  這樣做最大的好處就是可以自己很輕鬆的控制XML格式,最開始的時候我是通過新增引用的方式去呼叫某個方法一直失敗,但是用soapUI去測試這個方法又是可以成功呼叫的,折騰了半天,最後通過抓包的方式對傳送的資料進行對比,發現兩者傳送的XML相差甚遠,好了廢話不說了,就拿一個小例項來演示這個過程吧。

  首先,通過soapUI工具測試呼叫WebService裡一個名為getPopCheckedInfo的方法,生成的XML如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.pop.wsif.cogent.com/"> <soapenv:Header> <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-2" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username>使用者名稱</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">密碼</wsse:Password> <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">qTW5ajMAEp4o9BiSvcczNA==</wsse:Nonce> <wsu:Created>2010-05-24T07:02:10.531Z</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soapenv:Header> <soapenv:Body> <ws:getPopCheckedInfo> <arg0>引數</arg0> </ws:getPopCheckedInfo> </soapenv:Body> </soapenv:Envelope>

  上面三個用漢字標示的地方就是我們要修改賦值的地方,大家看到了吧,如果用新增引用自動生成代理類的方式,要產生這樣格式的XML有多難控制了吧,但是如果全部用程式碼來生成也不是一件容易的事,個人用了一個比較巧妙的辦法:
在專案中新增一個名為“getPopCheckedInfo”的xml檔案,將上面的XML貼上上去,然後再將這個XML檔案作為內嵌資源(在這個的檔案屬性裡面的‘生產操作’選擇‘嵌入的資源’),使用的時候直接載入這個XML檔案,然後修改那3個節點的值就可以了(使用者名稱和密碼一般都預先確定的,也可以直接寫在XML檔案裡,呼叫的時候就只要對那一個引數賦值了)。使用內嵌資源是為了不讓外面看到我們的那個XML檔案,以防被修改了什麼的。

  下面看看呼叫的程式碼實現吧:(為了理解方便清晰,我們用跟WebService上一模一樣的方法名和引數)

1 2 3 4 5 6 7 8 9 10 11 12 13 /// <summary> /// 根據居民id獲取該居民資訊 /// </summary> /// <param name="id">居民id</param> public static People getPopCheckedInfo(string id) { String ServerUrl = Config.GetWebServerURL();//得到WebServer地址 Hashtable pars = new Hashtable();//用來存放參數 pars["arg0"] = id; XmlDocument xml = WebSvcCaller.QuerySoapWebService(ServerUrl, "getPopCheckedInfo", pars); //這個是對返回的XML檔案處理,我刪掉了,處理完後返回一個居民的實體物件 return myPeople; }

WebSvcCaller.QuerySoapWebService方法程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /// <summary> /// 通用WebService呼叫(Soap),引數Pars為String型別的引數名、引數值 /// </summary> public static XmlDocument QuerySoapWebService(String URL, String MethodName, Hashtable Pars) { HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL); request.Method = "POST"; request.Accept = @"gzip,deflate"; request.ContentType = @"text/xml;charset=utf-8"; request.UserAgent = @"Jakarta Commons-HttpClient/3.1"; request.Credentials = CredentialCache.DefaultCredentials; request.Timeout = 10000; byte[] data = EncodeParsToSoap(Pars, MethodName); WriteRequestData(request, data);//將處理成位元組組的XML寫到流中傳送到服務端 XmlDocument doc = new XmlDocument(); doc = ReadXmlResponse(request.GetResponse());//讀取服務端返回的結果 return doc; }

EncodeParsToSoap(Pars, MethodName),處理XML檔案方法的程式碼:(以下僅供參考,大家根據自己的實際情況變動)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 /// <summary> /// 處理要傳送的XML文件 /// </summary> /// <param name="Pars">引數</param> /// <param name="MethodName">方法名</param> private static byte[] EncodeParsToSoap(Hashtable Pars, String MethodName) { XmlDocument xml = null; if (hshtableXML.ContainsKey(MethodName)) {//如果已經載入過,則從快取中讀取 xml = (XmlDocument)hshtableXML[MethodName]; } else {//如果還未載入則進行載入,並放入快取 //從資原始檔得到檔案流 Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("你的專案的名稱.XML檔案存放的資料夾." MethodName ".xml"); xml = new XmlDocument(); xml.Load(stream); hshtableXML.Add(MethodName, xml); } //修改引數的值 foreach (DictionaryEntry de in Pars) { XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable); nsmgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); nsmgr.AddNamespace("ws", "http://ws.pop.wsif.cogent.com/"); Hashtable subpars = de.Value as Hashtable; if (subpars == null) { string subNode = "soapenv:Envelope/soapenv:Body/ws:" MethodName "/" de.Key.ToString(); XmlNode node = xml.SelectSingleNode(subNode, nsmgr); node.InnerText = de.Value.ToString(); } else { foreach (DictionaryEntry subde in subpars) { string subNode = "soapenv:Envelope/soapenv:Body/ws:" MethodName "/" de.Key.ToString() "/" subde.Key.ToString(); XmlNode node = xml.SelectSingleNode(subNode, nsmgr); node.InnerText = subde.Value.ToString(); } } } //將修改後的XML檔案儲存到流中 //這樣做還可以保證傳送的XML檔案也是格式化的那種形式,而不是一整行 //如通過OuterXml獲取的就是一整行,這樣也可能會導致服務端解析失敗,個人這次就碰到這種情況了 MemoryStream outStream = new MemoryStream(); xml.Save(outStream); byte[] buffer = new byte[outStream.Length]; byte[] temp = outStream.GetBuffer(); for (int i = 0; i < buffer.Length; i ) { buffer[i] = temp[i]; } outStream.Close(); return buffer; }

最後還有WriteRequestData、ReadXmlResponse兩個方法的程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /// <summary> /// 寫到流中,傳送給服務端 /// </summary> /// <param name="request">HttpWebRequest連線物件</param> /// <param name="data">要寫入連線流發給服務端的內容</param> private static void WriteRequestData(HttpWebRequest request, byte[] data) { request.ContentLength = data.Length; Stream writer = request.GetRequestStream(); writer.Write(data, 0, data.Length); writer.Close(); } /// <summary> /// 讀取服務端返回的結果 /// </summary> private static XmlDocument ReadXmlResponse(WebResponse response) { StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); String retXml = sr.ReadToEnd(); sr.Close(); XmlDocument doc = new XmlDocument(); doc.LoadXml(retXml); return doc; }