關於.NET中動態呼叫Web Service服務的方法心得。
介紹.NET中動態呼叫Web Service的相關技術文章。
在.NET中呼叫Web Service服務(WSDL)有兩種可行的方法:
1、通過Web 服務引用,在本地生成所要呼叫服務的類;(靜態方法)
2、通過給定的WSDL服務地址,動態生成Web Service服務類進行服務呼叫;(動態方法)
由於1中的方法大部分人都會經常用到,因此暫不討論。
對於2,實現上較為複雜,主要的過程為:
(1)讀取WSDL內容到記憶體中;
(2)根據WSDL內容,動態生成Web Service服務程式碼;
(3)使用動態編譯技術將生成的Web Service服務程式碼編譯為DLL;
(4)通過反射機制實現動態呼叫。
_________________________________________________________
有關這類技術請參考如下網站:
1、《動態呼叫 WebService》
地址:http://www.rainsts.net/article.asp?id=304
2、《DynWsLib》
地址:http://www.thinktecture.com/Resources/Software/DynWsLib/default.html
說明:
歐洲一家名位Thinktecture公司釋出的開源專案,實現了比較完整的動態呼叫Web Service的方法,最新版本為1.6,支援.NET 2.0。最可貴的是這是一個完全開源的專案,我們可以下載下來根據實際情況進行一些修改,以適應不同的需求。
3、WSE
地址:http://msdn2.microsoft.com/en-us/webservices/aa740663.aspx
說明:
微軟釋出的支援Web Service的工具包。目前最新版本為3.0。
_________________________________________________________
Java 與 .NET的互呼叫
這是本文重點要討論的話題。
由於專案需要,我們必須為客戶提供一個.NET的動態呼叫Web Service包,以實現對Oracle的BPEL伺服器上釋出的Web Service進行動態呼叫。
在使用動態DynWSLib生成的物件進行呼叫的時候,發現只能夠傳送一次SOAP,當第二次傳送之後,.NET程式會丟擲連結已被斷開的異常。
這樣的錯誤十分詭異,在網上搜索了之後,發現問題在於BPEL伺服器與Microsoft的IIS之間是有區別的,看來微軟還是很喜歡搞壟斷。
下面說說個人分析的結論,共大家參考:
問題的出現可能和微軟的地層支援有關,本人猜測,微軟生成的Web Service物件在傳送SOAP請求時,建立的HTTP連結在請求傳送完成之後,會長時間保持連結狀態(即連結沒有立即斷開),而對於Oracle的Web伺服器,當相應SOAP請求之後,會主動斷開HTTP連結,這也許就是為什麼用微軟的東西傳送SOAP訊息給Oracle伺服器,第一次能夠成功,第二次就會報連結已斷開,傳送失敗的錯誤。後面的解決方案中也部分支援了我的這個觀點。
________________________________________________________
解決方案:
WSE + HTTP
1、使用WSE構造SOAP請求訊息;
2、使用HTTP傳送SOAP訊息。
下面給出部分參考程式碼:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace My.Web.WebService
{
internal class SoapHttpClient
{
private string url = null;
public SoapHttpClient(string url)
{
this.url = url;
}
public string RequestResponse(string methodName, string envelope)
{
// 用於支援SSL
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(OnCheckRemoteCallback);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
// 必須設定該值為flase,否則出錯
request.KeepAlive = false;
request.Method = "POST";
request.ContentType = "text/xml";
request.Headers.Add("SOAPAction", methodName);
UTF8Encoding encoding = new UTF8Encoding();
byte[] bodyBytes = encoding.GetBytes(envelope);
request.ContentLength = bodyBytes.Length;
using (Stream serviceRequestBodyStream = request.GetRequestStream())
{
serviceRequestBodyStream.Write(bodyBytes, 0, bodyBytes.Length);
serviceRequestBodyStream.Close();
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
string result = reader.ReadToEnd();
return result;
}
}
}
}
/// <summary>
/// Using untrusted SSL certificates
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
private static bool OnCheckRemoteCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
}
_________________________________________________________
[b]最後討論一下有關DynWSLib動態呼叫的問題。[/b]
使用DynWSLib在呼叫Web Service服務的時候,需要對傳入的引數進行適當的處理。
1、獲取所呼叫的方法的引數型別。
/// <summary>
/// 獲取引數型別。該方法只獲取第一個引數的型別。
/// </summary>
/// <returns></returns>
private Type GetParameterType()
{
Type t = wsp.Instance.GetType();
MethodInfo methodInfo = t.GetMethod(wsp.MethodName);
// 獲得方法的引數型別
ParameterInfo[] param = methodInfo.GetParameters();
if (param.Length < 1)
return null;
// 例項化引數型別
Type objType = param[0].ParameterType;
return objType;
}
2、新增引數到呼叫方法中。
private DynamicWebServiceProxy wsp = null;
// For SOAP Body Parameters.
private IDictionary paramsTable = new Hashtable();
public void AddParameter(string name, object value)
{
Type objType = GetParameterType();
// 處理字串型別
if (objType == "".GetType())
{
wsp.AddParameter(value);
}
else // 處理物件型別
{
object obj = Activator.CreateInstance(objType);
FieldInfo field = obj.GetType().GetField(name);
Type filedType = field.FieldType;
// 對基本型別和泛型區別處理
object valueObj = null;
if (filedType.IsGenericType)
{
Type[] typeParameters = filedType.GetGenericArguments();
Type valueType = Type.GetType(typeParameters[0].FullName);
valueObj = Convert.ChangeType(value, valueType);
}
else
{
valueObj = Convert.ChangeType(value, filedType);
}
field.SetValue(obj, valueObj);
wsp.AddParameter(obj);
}
}