[WCF]设置拦截器捕捉到request和reply消息

在熟练掌握了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! 我们成功的拦截了请求,并将请求信息打印了出来。

 

总结,有了这个拦截器,我们可以做很多的事情,比如修改消息头和消息体,计算消息的大小(流量统计),统计服务调用的次数和平均时间,客户端情况,等等。

posted @ 2017-06-22 10:49  杨浪  阅读(1619)  评论(0编辑  收藏  举报