[WCF]設置攔截器捕捉到request和reply消息


WCF進階學習ing...

在熟練掌握了ABC的使用以后,就開始想着去了解WCF是怎么通信的了。首先是服務描述語言wsdl,它定義了服務的描述等等,用於讓外界知道這個服務的ABC是什么。另外一個比較重要的就是消息。

WCF是通過消息進行通訊的,一般是使用SOAP形式。服務端的信道監聽器接收到消息之后,對消息進行反序列化,解碼,然后通過激活對象,再去invoke相應的操作,操作的結果(返回值)再通過編碼,序列化,傳送給調用者,調用者再對消息進行反序列化,解碼,最后拿到結果。所以在這個過程中,對消息的理解和熟悉對於我們理解WCF的操作流程是很大的幫助的。

然后我們就開始攔截這個消息來看看這個消息到底是什么。。。廢話不多說,上code。。

首先創建一個提供復數計算的服務,使用共享C的方式,項目結構如下,服務端和客戶端都是一個控制台程序

 

服務契約代碼 IComplexCalculate.cs:

 1 namespace Cookiezhi.WcfStudy.Contracts.ServiceContracts
 2 {
 3     [ServiceContract(Namespace="http://www.cookiezhi.com/service/complex")]
 4     public interface IComplexCalculate
 5     {
 6         /// <summary>
 7         /// 8         /// </summary>
 9         [OperationContract]
10         Complex Add(Complex a, Complex b);
11 
12         /// <summary>
13         ///14         /// </summary>
15         [OperationContract]
16         Complex Subtract(Complex a, Complex b);
17 
18         /// <summary>
19         ///20         /// </summary>
21         [OperationContract]
22         Complex Multiply(Complex a, Complex b);
23 
24         /// <summary>
25         /// 取模
26         /// </summary>
27         [OperationContract]
28         double Modulus(Complex a);
29     }
30 }

數據契約 Complex.cs:

 1 namespace Cookiezhi.WcfStudy.Contracts.DataContracts
 2 {
 3     [DataContract(Namespace = "http://www.cookiezhi.com/data/complex")]
 4     public class Complex
 5     {
 6         /// <summary>
 7         /// 實數
 8         /// </summary>
 9         [DataMember]
10         public double A { get; set; }
11 
12         /// <summary>
13         /// 虛數
14         /// </summary>
15         [DataMember]
16         public double B { get; set; }
17     }
18 }

服務契約實現 ComplexCalculateService:

 1 namespace Cookiezhi.WcfStudy.Services
 2 {
 3     public class ComplexCalculateService : IComplexCalculate
 4     {
 5         public Complex Add(Complex a, Complex b)
 6         {
 7             return new Complex()
 8             {
 9                 A = a.A + b.A,
10                 B = a.B + b.B
11             };
12         }
13 
14         public Complex Subtract(Complex a, Complex b)
15         {
16             return new Complex()
17             {
18                 A = a.A - b.A,
19                 B = a.B - b.B
20             };
21         }
22 
23         public Complex Multiply(Complex a, Complex b)
24         {
25             return new Complex()
26             {
27                 A = a.A * b.A - a.B * b.B,
28                 B = a.A * b.B + a.B * b.A
29             };
30         }
31 
32         public double Modulus(Complex a)
33         {
34             return Math.Sqrt(a.A * a.A + a.B * a.B);
35         }
36     }
37 }

采用配置文件方式去設置服務 app.config:

 1 <system.serviceModel>
 2     <behaviors>
 3       <serviceBehaviors>
 4         <behavior name="mexBehavior">
 5           <serviceMetadata httpGetEnabled="true"/>
 6         </behavior>
 7       </serviceBehaviors>
 8     </behaviors>
 9     
10     <services>
11       <service name="Cookiezhi.WcfStudy.Services.ComplexCalculateService" behaviorConfiguration="mexBehavior">
12         <endpoint address="" binding="basicHttpBinding" contract="Cookiezhi.WcfStudy.Contracts.ServiceContracts.IComplexCalculate" />
13         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
14         <host>
15           <baseAddresses>
16             <add baseAddress="http://127.0.0.1:9999/complexcalcservice"/>
17           </baseAddresses>
18         </host>
19       </service>
20     </services>
21     
22   </system.serviceModel>

然后服務端的main方法里啟動服務:

 1 namespace Cookiezhi.WcfStudy.Hosting
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             using(ServiceHost host = new ServiceHost(typeof(ComplexCalculateService)))
 8             {
 9                 host.Opened += delegate
10                 {
11                     Console.WriteLine("Service {0} started", host.Description.Name);
12                 };
13 
14                 host.Open();
15 
16                 Console.ReadKey();
17             }
18         }
19     }
20 }

OK, 服務端好了,我們啟動一下

再看一下WSDL

 

OK是好的,這些對於做過WCF相關的朋友們都是輕車熟路了,下面是客戶端,通過配置文件加ChannelFactory方式來創建並調用

App.config

1 <system.serviceModel>
2     <client>
3       <endpoint name="ComplexCalculateService" address="http://127.0.0.1:9999/complexcalcservice" binding="basicHttpBinding" contract="Cookiezhi.WcfStudy.Contracts.ServiceContracts.IComplexCalculate" />
4     </client>
5   </system.serviceModel>

Main方法

 1 namespace Cookiezhi.WcfStudy.Client
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             using(ChannelFactory<IComplexCalculate> factory = new ChannelFactory<IComplexCalculate>("ComplexCalculateService"))
 8             {
 9                 IComplexCalculate proxy = factory.CreateChannel();
10 
11                 Complex a = new Complex() { A = 1, B = 2 };
12                 Complex b = new Complex() { A = 2, B = 1 };
13                 Complex result = null;
14 
15                 result = proxy.Add(a, b);
16                 Console.WriteLine("Add result is {0} + {1}i", result.A, result.B);
17 
18                 Console.ReadKey();
19             }
20         }
21     }
22 }

調用服務:

 

前戲做完了,我們開始進入主題:

我們需要攔截消息,並把消息打印出來,那么我們就需要一個攔截器,叫做MessageInspector,WCF為我們提供了兩種攔截器:

客戶端攔截器 IClientMessageInspector

提供兩個接口

BeforeSendRequest:向服務器發送請求前執行

AfterReceiveReply:接收到服務器的回復消息后執行

服務端攔截器 IDispatchMessageInspector

他也提供兩個接口

AfterReceiveRequest:invoke操作之前執行

BeforeSendReply:發送reply給客戶端之前執行

 

在這里我們在服務端設置個攔截器,然后打印出請求和回復的消息,所以我們使用IDispatchMessageInspector這個接口

實現接口 MessageInspector.cs

 1 namespace Cookiezhi.WcfStudy.Hosting.MessageInspect
 2 {
 3     public class MessageInspector : IDispatchMessageInspector
 4     {
 5         public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
 6         {
 7             Console.WriteLine(request.ToString());
 8             return DateTime.Now;
 9         }
10 
11         public void BeforeSendReply(ref Message reply, object correlationState)
12         {
13             Console.WriteLine(reply.ToString());
14             DateTime requestTime = (DateTime)correlationState;
15 
16             var duration = DateTime.Now - requestTime;
17             Console.WriteLine(duration);
18         }
19     }
20 }

其中AfterReceiveRequest先執行,然后去執行遠程方法,然后再執行BeforeSendReply,所以在這里加了一個操作計時的功能(可選)。

然后我們要將這個攔截器給寄宿在我們的終結點上,所以需要定義一個終結點行為(EndpointBehavior),並寄宿在服務上。

MessageInspectorBehavior.cs,在ApplyDispatchBehavior方法實現中將我們新建的Inspector實例加到dispatcher的MessageInspectors中

 1 namespace Cookiezhi.WcfStudy.Hosting.MessageInspect
 2 {
 3     public class MessageInspectorBehavior : IEndpointBehavior
 4     {
 5         public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
 6         {
 7         }
 8 
 9         public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
10         {
11         }
12 
13         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
14         {
15             endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageInspector());16         }
17 
18         public void Validate(ServiceEndpoint endpoint)
19         {
20         }
21     }
22 }

最后創建一個配置元素用於在配置文件中給終結點配置這個行為.

 1 namespace Cookiezhi.WcfStudy.Hosting.MessageInspect
 2 {
 3     public class MessageInspectorExtensionElement : BehaviorExtensionElement
 4     {
 5         public override Type BehaviorType
 6         {
 7             get { return typeof(MessageInspectorBehavior); }
 8         }
 9 
10         protected override object CreateBehavior()
11         {
12             return new MessageInspectorBehavior();
13         }
14     }
15 }

下面就是配置這個行為了

App.config

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3 
 4   <system.serviceModel>
 5     <extensions>
 6       <behaviorExtensions>
 7         <add name="messageInspector" type="Cookiezhi.WcfStudy.Hosting.MessageInspect.MessageInspectorExtensionElement, Cookiezhi.WcfStudy.Hosting"/>
 8       </behaviorExtensions>
 9     </extensions>
10     
11     <behaviors>
12       <serviceBehaviors>
13         <behavior name="mexBehavior">
14           <serviceMetadata httpGetEnabled="true"/>
15         </behavior>
16       </serviceBehaviors>
17       <endpointBehaviors>
18         <behavior name="messageInspector">
19           <messageInspector />
20         </behavior>
21       </endpointBehaviors>
22     </behaviors>
23     
24     <services>
25       <service name="Cookiezhi.WcfStudy.Services.ComplexCalculateService" behaviorConfiguration="mexBehavior">
26         <endpoint address="" binding="basicHttpBinding" contract="Cookiezhi.WcfStudy.Contracts.ServiceContracts.IComplexCalculate" behaviorConfiguration="messageInspector" />
27         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
28         <host>
29           <baseAddresses>
30             <add baseAddress="http://127.0.0.1:9999/complexcalcservice"/>
31           </baseAddresses>
32         </host>
33       </service>
34     </services>
35     
36   </system.serviceModel>
37   
38     <startup> 
39         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
40     </startup>
41 </configuration>

客戶端的代碼不要做出任何的改變,

然后我們嘗試一下

 

Great! 我們成功的攔截了請求,並將請求信息打印了出來。

 

總結,有了這個攔截器,我們可以做很多的事情,比如修改消息頭和消息體,計算消息的大小(流量統計),統計服務調用的次數和平均時間,客戶端情況,等等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM