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

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

  • 相关阅读:
    js高级程序设计AJAX && JSON
    python核心高级学习总结7正则表达式
    python核心高级学习总结8动态性、__slots__、生成器、迭代器、装饰、闭包
    python核心高级学习总结3python实现进程的三种方式及其区别
    python核心高级学习总结6面向对象进阶之元类
    运维术语名词
    资源分享编程、数据库、安全、运维等
    python之Bug之字符串拼接bug
    CSS hack:实现IE6、IE7、Firefox兼容(转摘)
    (装载) Web开发技术的历史发展简介
  • 原文地址:https://www.cnblogs.com/cookiezhi/p/4922027.html
Copyright © 2020-2023  润新知