在熟练掌握了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 ///7 /// 加 8 /// 9 [OperationContract]10 Complex Add(Complex a, Complex b);11 12 ///13 /// 减14 /// 15 [OperationContract]16 Complex Subtract(Complex a, Complex b);17 18 ///19 /// 乘20 /// 21 [OperationContract]22 Complex Multiply(Complex a, Complex b);23 24 ///25 /// 取模26 /// 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 ///7 /// 实数 8 /// 9 [DataMember]10 public double A { get; set; }11 12 ///13 /// 虚数14 /// 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.B11 };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.B20 };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.A29 };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:
12 3 9 104 85 76 11 21 2212 2013 14 15 1916 1817
然后服务端的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 += delegate10 {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
12 3 54
Main方法
1 namespace Cookiezhi.WcfStudy.Client 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 using(ChannelFactoryfactory = new ChannelFactory ("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 23 4 5 37 386 10 117 98 12 23 2413 1714 1615 18 2219 2120 25 35 3626 3427 28 29 3330 3231 39 4140
客户端的代码不要做出任何的改变,
然后我们尝试一下
Great! 我们成功的拦截了请求,并将请求信息打印了出来。
总结,有了这个拦截器,我们可以做很多的事情,比如修改消息头和消息体,计算消息的大小(流量统计),统计服务调用的次数和平均时间,客户端情况,等等。