轉眼你已經學了三天的wcf了,是不是很好奇wcf在傳輸層上面到底傳遞的是個什麼鳥毛東西呢???應該有人知道是soap,那soap這叼毛長得是什麼
樣呢?這一篇我們來揭開答案。。。
一:soap到底長成什麼樣子
為了能看清soap長的啥樣,我可以用強大的Fiddler來監視一下,突然好激動啊!!!
1.Server
1 static void Main(string[] args) 2 { 3 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:19200")); 4 5 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie"); 6 7 //公佈後設資料 8 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 9 10 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 11 12 host.Open(); 13 14 Console.WriteLine("服務已經開啟。。。"); 15 16 Console.Read(); 17 }
2.Client
1 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 2 3 var client = factory.CreateChannel(); 4 5 client.Update("王八蛋");
現在我想你大概看清楚了這玩意是個麼樣子,一個建立在xml上面的一種訊息格式,根元素是envelope,我知道這叼毛翻譯過來就是“信封”,所以就有了”封頭“
和”封體”,就是s:Header 和 s:Body,從這個soap中你可以看到它忽略了header,然後我們繼續往下看,還記得Update的意思嗎???如果你讀懂了上一篇,
你應該知道這是一個Action,也就是所謂的input訊息。與之對應的就是UpdateResponse這個output訊息,對吧,還記得xmlns="http://tempuri.org/">嗎?
它就是IHomeService的預設名稱空間,對吧。。。
下一個我們關注的是Update這個Action中的<str>這個,你也看得到,這個就是上圖中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中的名稱
1 [DataContract(Namespace = "http://tempuri.org/", Name = "Update")] 2 class Test 3 { 4 [DataMember] 5 public string str { get; set; } 6 }
然後,我把這個資料契約塞到envelope中的body中,如下:
1 BasicHttpBinding bingding = new BasicHttpBinding(); 2 3 BindingParameterCollection param = new BindingParameterCollection(); 4 5 var u = new Test() { str = "王八蛋" }; 6 7 Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u); 8 9 IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param); 10 11 factory.Open(); 12 13 IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 14 15 channel.Open(); 16 17 var result = channel.Request(request); 18 19 channel.Close(); 20 21 factory.Close();
接下來,我們跑起來看一下,效果咋樣。。。
看沒看到,這個就是我手工構造的Message,是不是太帥了。。。哈哈,太帥的應該在後面,剛才也說了,既然大家玩的都是Message,而你這個幾把wcf卻僅僅把
我的message.body拿出來了,那乾脆我直接在契約方法中加message豈不是更好麼???自由操作Message還有個什麼好處呢??當然啦,我可以在Message的
Header中加一些引數token,client的ip地址,client的身份,client的時間等等這些統計資訊,對吧。。。這樣才是最帥的,好了,說幹就幹,我們修改下server端的
契約方法,只用來接受Message。
server端:
1 public class HomeService : IHomeService 2 { 3 public Message Update(Message message) 4 { 5 var header = message.Headers; 6 7 var ip = header.GetHeader<string>("ip", string.Empty); 8 9 var currentTime = header.GetHeader<string>("currenttime", string.Empty); 10 11 //這個就是牛逼的 統計資訊。。。 12 Console.WriteLine("客戶端的IP=" + ip + " 當前時間=" + currentTime); 13 14 return Message.CreateMessage(message.Version, message.Headers.Action + "Response", "等我吃完肯德基,再打死你這個傻逼!!!"); 15 } 16 }
client端:
1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 BasicHttpBinding bingding = new BasicHttpBinding(); 8 9 BindingParameterCollection param = new BindingParameterCollection(); 10 11 var u = new Test() { str = "王八蛋" }; 12 13 Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u); 14 15 //在header中追加ip資訊 16 request.Headers.Add(MessageHeader.CreateHeader("ip", string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString())); 17 request.Headers.Add(MessageHeader.CreateHeader("currenttime", string.Empty, DateTime.Now)); 18 19 IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param); 20 21 factory.Open(); 22 23 IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 24 25 channel.Open(); 26 27 var result = channel.Request(request); 28 29 channel.Close(); 30 31 factory.Close(); 32 } 33 } 34 35 [DataContract(Namespace = "http://tempuri.org/", Name = "Update")] 36 class Test 37 { 38 [DataMember] 39 public string str { get; set; } 40 } 41 }
然後我們用Fiddler監視一下結果:
現在一切都如我所願,好了,我想你也大概明白了這個神奇的message,也不要忘了它就是wcf的基本通訊單元,我要去吃肯德基了。。。。。。