天天看點

對WebService的一些封裝技巧總結

http://www.cnblogs.com/sxwgf/archive/2011/07/10/something-about-webservice.html

今天早上起來,想談談.NET中的WebService,當然我不想講什麼是 WebService,或者怎麼用WebService,因為那個大家 随便Google一下前100頁都能找到答案。今天我想來分享一下我在用WebService中的一些技巧(至少我認為是技巧,還有點成就感),希望能給 大家以後在用WebService時一點幫助和啟發吧。

一、問題誕生 -- 大部分解決方案的背後總是一些頭痛的問題

很早以前就用過傳說中的WebService,但一直是用正常的思路在用:建立WebService項目-->寫Web服務方法--> 在項目中添加Web引用-->調用Web方法。這樣貌似很好,非常符合規範,在一段時間内效果也還可以,但漸漸的随着項目的擴大和同時參與項目的人 員增多,就越來越覺得這種正常的方法很是不爽,為什麼呢?我每次修改WebService端(添加、删除Web方法,以及修改方法名稱),在引用端我都要 更新WebService引用,其實是就是更新WSDL檔案,很是煩人。

二、化分為合 -- 傳說分久必合,合久必分

好吧,既然增加、删除、修改web方法名都會引起WSDL的更新,那麼我們索性用一個統一的方法來作為webservice的通路入口吧,然後内部用switch case來區分調用哪個方法,先貼代碼吧,再來簡單講講:

統一通路接口IRemoteCall:

public interface IRemoteCall

{

byte[] GeneralCall(string methodName, params byte[] param);

}

然後定義一個WebService并實作以上接口(以前還沒真在webservice上實作過接口,哈哈):

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[ToolboxItem(false)]

public class BlogService : System.Web.Services.WebService, IRemoteCall

[WebMethod(EnableSession = true)]

public byte[] GeneralCall(string methodName, params byte[] param)

switch (methodName)

case "LoadBlog":

long blogId = Serializer.DeserializeToObject<long>(param);

BLLBlogArtical ba = new AppBlog().LoadBlog(blogId);

return Serializer.SerializeToBinary(ba);

case "DeleteBlog":

//To Do Your Code

return null;

這裡為什麼要定義接口IRemoteCall呢,主要是為接下來統一調用webservice服務的,所有實作這個接口的webservice類都可以通 過GeneralCall來完成調用,待會将webservice通路器的時候會具體講到,這裡主要講講這個switch case。

這裡我們定義了一個統一的通路入口

byte[] GeneralCall(string methodName,params byte[] param)

意思是:傳入要調用的方法名稱以及序列化後的參數,傳回序列化後的結果。這裡為了統一資料,我們均對參數和傳回值都序列化成byte數組,即用Serializer.SerializeToBinary(object)來實作,這樣所有調用就都統一了格式。

有人可能會提出質疑,這樣方法名稱都已字元串形式是不是會顯得難看,而且字元串容易出錯,還沒有智能提示?那也好解決,我們可以把方法名稱定義成const常量就可以了。這裡我對webservice的一個态度是:webservice層就是完成轉接和排程工作的,它僅僅起到承接的作用,用了他可以将服務任意分布,是以裡面是沒有任何邏輯的(邏輯都是被封裝在其他dll中的),最多是一些資料轉換,是以我采用了這種模糊接口的方式。

三、自定義webservice通路器,爽死用戶端

上面我們完成了webservice端的工作,接下來就來實作用戶端對webservice的靈活調用,這裡上面定義的那個IRemoteCall就起到作用了,首先我們定義一個webservice通路器類RemoteCaller,代碼如下:

View Code

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Web.Services.Protocols;

using SharedLib_403;

namespace ITIvy.Shared.RemoteCaller

/// <summary>

/// 遠端接口通路器

/// </summary>

public class RemoteCaller

private string _MethodName;

private byte[] _ParamByte;

private IRemoteCall _Caller;

private ArrayList _Params;

/// 參數清單

public ArrayList Params

get { return _Params; }

set { _Params = value; }

/// 序列化後的參數

public byte[] ParamByte

get { return _ParamByte; }

set { _ParamByte = value; }

/// 遠端服務方法名稱

public string MethodName

get { return _MethodName; }

set { _MethodName = value; }

/// 遠端服務調用接口

public IRemoteCall Caller

get { return _Caller; }

set { _Caller = value; }

/// 構造

/// <param name="caller">Webservice遠端接口</param>

public RemoteCaller(IRemoteCall caller)

_Caller = caller;

_Params = new ArrayList();

/// 調用遠端接口

/// <param name="methodName">方法名稱</param>

/// <param name="param">參數對象</param>

/// <returns></returns>

public byte[] Call(string methodName, object param)

try

_MethodName = methodName;

_ParamByte = Serializer.SerializeToBinary(param);

return _Caller.GeneralCall(_MethodName, _ParamByte);

catch (Exception ex)

if (ex is SoapException)

throw new Exception(((SoapException)ex).Detail["Message"].InnerText);

else

throw ex;

/// <param name="param">參數清單</param>

public byte[] Call(string methodName, ArrayList param)

_Params = param;

_ParamByte = Serializer.SerializeToBinary(_Params);

/// <param name="param">參數對象數組</param>

public byte[] Call(string methodName, params object[] param)

foreach (object obj in param)

_Params.Add(obj);

public byte[] Call()

if (string.IsNullOrEmpty(_MethodName))

throw new Exception("遠端方法不能為空!");

/// <typeparam name="T">傳回值類型</typeparam>

public T Call<T>()

byte[] resultByte = Call();

return Serializer.DeserializeToObject<T>(resultByte);

public T Call<T>(string methodName, ArrayList param)

byte[] resultByte = Call(methodName, param);

public T Call<T>(string methodName, object param)

byte[] resultByte = _Caller.GeneralCall(_MethodName, _ParamByte);

public T Call<T>(string methodName, params object[] param)

這個通路器主要是定義了一系列通路接口的重載,利用了c#的泛型更加使接口簡單了。哈哈,這個類就能讓我們實作一句話調用webservice,相 當簡潔。注意裡面的IRemoteCall屬性,就是隻要傳入實作了該接口的類,就都可以通過該通路器來通路webservice。如何使用該類呢,下面 給一個例子吧:

IRemoteCall Caller = new BlogService.BlogService();

BLLBlogArtical bllArtical = new RemoteCaller(Caller).Call<BLLBlogArtical>("LoadBlog", id);

抱歉,說錯了,要兩句話來調用,但是這裡少去了很多資料轉換的工作,因為有了泛型,呵呵,而且我可以在RemoteCaller這個通路器類中做很多工作,比如異常處理,權限驗證等等。

四、總結 -- 寫了這麼多不總結可不行

這個實作方法的核心在于用IRemoteCall接口來規範webservice類的實作方式均為統一GenerateCall,然後 webservice類中通過switch case來将所有方法整合在一起,避免頻繁更新WSDL的麻煩,最後用戶端利用IRemoteCall定義一個webservice通路器類 RemoteCaller來提供統一的webservice通路。小技巧,呵呵,請大家笑納,有什麼不對的地方請指出...

不好意思,上面引用的那個SharedLib_403中有序列化和反序列化的代碼,一時忘記貼出來了,現在補上:

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

using System.Xml;

namespace SharedLib_403

public class Serializer

/// 把對象序列化成二進制流

/// <param name="obj">需要序列化的對象</param>

public static byte[] SerializeToBinary(object obj)

if (obj == null)

MemoryStream stream = new MemoryStream();

new BinaryFormatter().Serialize(stream, obj);

stream.Position = 0L;

byte[] buffer = new byte[stream.Length];

stream.Read(buffer, 0, buffer.Length);

stream.Close();

return buffer;

/// 把二進制流反序列化成對象

/// <param name="bytes">需要反序列化的二進制流</param>

public static object DeserializeToObject(byte[] bytes)

object obj = null;

if (bytes != null)

MemoryStream stream = new MemoryStream(bytes);

obj = new BinaryFormatter().Deserialize(stream);

return obj;

/// <typeparam name="T">對象類型</typeparam>

public static T DeserializeToObject<T>(byte[] bytes)

object obj = DeserializeToObject(bytes);

return (T)obj;

本文轉自cnn23711151CTO部落格,原文連結:http://blog.51cto.com/cnn237111/617461 ,如需轉載請自行聯系原作者