轉眼你已經學了三天的wcf了,是不是很好奇wcf在傳輸層上面到底傳遞的是個什么鳥毛東西呢???應該有人知道是soap,那soap這叼毛長得是什么
樣呢?這一篇我們來揭開答案。。。
## 一:soap到底長成什么樣子
為了能看清soap長的啥樣,我可以用強大的Fiddler來監視一下,突然好激動啊!!!
1.Server
~~~
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:19200"));
host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie");
//公布元數據
host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
Console.WriteLine("服務已經開啟。。。");
Console.Read();
}
~~~
2.Client
~~~
ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
var client = factory.CreateChannel();
client.Update("王八蛋");
~~~

現在我想你大概看清楚了這玩意是個么樣子,一個建立在xml上面的一種消息格式,根元素是envelope,我知道這叼毛翻譯過來就是“信封”,所以就有了”封頭“
和”封體”,就是s:Header 和 s:Body,從這個soap中你可以看到它忽略了header,然后我們繼續往下看,還記得Update的意思嗎???如果你讀懂了上一篇,
你應該知道這是一個Action,也就是所謂的input消息。與之對應的就是UpdateResponse這個output消息,對吧,還記得xmlns="http://tempuri.org/">嗎?
它就是IHomeService的默認命名空間,對吧。。。

下一個我們關注的是Update這個Action中的這個,你也看得到,這個就是上圖中Update方法中的str參數,最后我們來看一下UpdateResponse中
的<UpdateResult xmlns:a="http://schemas.datacontract.org/2004/07/MyService,不知道你是否還記得它就是WSDL中關于Student的XSD結
構,看下圖:

好了,wcf中的soap結構我們也大概了解了一下,不知道有沒有引發你對soap更深入的思考呢???
## 二:對soap的更深入思考
通過fiddler觀察,你應該也明白了,不管是客戶端還是服務端,wcf的高層封裝都是僅僅拿出了Envelope中的body節點,而其他節點對我們來說好像并
沒有什么卵用,比如我說的Header節點,這么說來,Header是不是有點浪費呢???那下面有一個問題來了,wcf在底層用什么來構造消息的呢???下面
我們大概找下client端的源碼。。。

通過上面的圖,你現在應該也知道了在.net中其實tmd的就是message構造的,所以我想告訴你的是:既然wcf在底層也是用message來構造的,何不我自己
就來構造message消息呢???豈不美哉???這樣我就可以隨意操作message,對吧。。。不然wcf這個高層封裝的叼毛,對我來說就是一種束縛。。。因
為我已經知道了service公布的wsdl,所以我可以輕松構造message。。。
## 三:用message來調用Server端
? 廢話不多說,構造message你一定要知道下圖中的三點:(input這個Action,契約方式 和 服務地址)。

好了,下面我先來構造數據契約,指定服務契約的命名空間 和 Action在Soap中的名稱
~~~
[DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
class Test
{
[DataMember]
public string str { get; set; }
}
~~~
然后,我把這個數據契約塞到envelope中的body中,如下:
~~~
BasicHttpBinding bingding = new BasicHttpBinding();
BindingParameterCollection param = new BindingParameterCollection();
var u = new Test() { str = "王八蛋" };
Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);
IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
factory.Open();
IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
channel.Open();
var result = channel.Request(request);
channel.Close();
factory.Close();
~~~
接下來,我們跑起來看一下,效果咋樣。。。

看沒看到,這個就是我手工構造的Message,是不是太帥了。。。哈哈,太帥的應該在后面,剛才也說了,既然大家玩的都是Message,而你這個幾把wcf卻僅僅把
我的message.body拿出來了,那干脆我直接在契約方法中加message豈不是更好么???自由操作Message還有個什么好處呢??當然啦,我可以在Message的
Header中加一些參數token,client的ip地址,client的身份,client的時間等等這些統計信息,對吧。。。這樣才是最帥的,好了,說干就干,我們修改下server端的
契約方法,只用來接受Message。
server端:
~~~
public class HomeService : IHomeService
{
public Message Update(Message message)
{
var header = message.Headers;
var ip = header.GetHeader<string>("ip", string.Empty);
var currentTime = header.GetHeader<string>("currenttime", string.Empty);
//這個就是牛逼的 統計信息。。。
Console.WriteLine("客戶端的IP=" + ip + " 當前時間=" + currentTime);
return Message.CreateMessage(message.Version, message.Headers.Action + "Response", "等我吃完肯德基,再打死你這個傻逼!!!");
}
}
~~~
client端:
~~~
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
BasicHttpBinding bingding = new BasicHttpBinding();
BindingParameterCollection param = new BindingParameterCollection();
var u = new Test() { str = "王八蛋" };
Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u);
//在header中追加ip信息
request.Headers.Add(MessageHeader.CreateHeader("ip", string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString()));
request.Headers.Add(MessageHeader.CreateHeader("currenttime", string.Empty, DateTime.Now));
IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param);
factory.Open();
IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie"));
channel.Open();
var result = channel.Request(request);
channel.Close();
factory.Close();
}
}
[DataContract(Namespace = "http://tempuri.org/", Name = "Update")]
class Test
{
[DataMember]
public string str { get; set; }
}
}
~~~
然后我們用Fiddler監視一下結果:

現在一切都如我所愿,好了,我想你也大概明白了這個神奇的message,也不要忘了它就是wcf的基本通信單元,我要去吃肯德基了。。。。。。
- 第一天 三種Binding讓你KO80%的業務
- 第二天 告別煩惱的config配置
- 第三天 client如何知道server提供的功能清單
- 第四天 你一定要明白的通信單元Message
- 第五天 你需要了解的三個小技巧
- 第六天 你必須要了解的3種通信模式
- 第七天 Close和Abort到底該怎么用才對得起觀眾
- 第八天 對“綁定”的最后一點理解
- 第九天 高級玩法之自定義Behavior
- 第十天 學會用SvcConfigEditor來簡化配置
- 第十一天 如何對wcf進行全程監控
- 第十二天 說說wcf中的那幾種序列化
- 第十三天 用WCF來玩Rest
- 第十四天 一起聊聊FaultException
- 終結篇 那些你需要注意的坑