[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! 我们成功的拦截了请求,并将请求信息打印了出来。
总结,有了这个拦截器,我们可以做很多的事情,比如修改消息头和消息体,计算消息的大小(流量统计),统计服务调用的次数和平均时间,客户端情况,等等。


浙公网安备 33010602011771号