1. 程式人生 > >關於.NET中動態呼叫Web Service服務的方法心得。

關於.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);
    }
}