WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇]

Part I

中,我们创建了一个InterceptService,并且通过一个特殊的EndpointBehavior,ClientViaBehavior实现了message的拦截、转发功能。在本节中,我们将讨论另外一种不同的实现方式。如何说ClientViaBehavior是基于Client端的实现方式,那么我们今天讨论的是基于Service的实现方式。

在对新的实现方式展开介绍之前,我们先来介绍一下关于逻辑地址和物理地址。

一、逻辑地址和物理地址

我们知道,WCF通过Endpoint进行通信:service
provider将service通过一个或多个Endpoint暴露给潜在的service consumer;service
consumer通过与之匹配的endpoint调用service。众所周知,Endpoint=Address+Binding+Contract,而Address代表了service的地址,解决service的寻址问题。一般来讲,我们把这个地址称为逻辑地址。而物理地址代表client端最终发送message的目标地址,以及service端实际的监听地址(Listen
Address)

  • 对于client端来说:一般来说,它所访问的service的地址与message最终的发送的目标地址是一样的。但是有时候也不一定相同,比如在Part

    I的例子中,service的地址是http://127.0.0.1:9999/calculateservice(逻辑地址),而最终我们的message却被发送到http://127.0.0.1:8888/interceptservice(物理地址)。通过ClientViaBehavior我们可以轻易地实现物理地址与逻辑地址不同的场景。

  • 对于service端来说:一般情况下service的地址(逻辑地址)与ChannelListener监听地址(物理地址)是一样的。但是在特殊的情况下也不尽相同。他能够Endpoint的ListenUri属性,我们很容易的使用一个与逻辑地址不同的监听地址。这就是我们接下来需要讨论的问题。

二、实现原理解析

不同于ClientViaBehavior的解决方案,我们今天讨论的方案是通过制定InterceptService或者CalcualteService
Endpoint的ListenUri来实现的。而具体的实现方案,我们又有两种不同的方式,我们先来讨论这两种方式的实现原理:

1、方案一

如上图所示,client与一般的service的调用别无二致,通过CalculateService的逻辑地址(http://127.0.0.1:9999/calculateservice)进行访问;不过CalculateService和InterceptService的ListenAddress去互换一下:CalculateService-http://127.0.0.1:8888/interceptservice,InterceptService-http://127.0.0.1:9999/calculateservice。那么Client发送到http://127.0.0.1:9999/calculateservice的Message将会被InterceptService截获,InterceptService再将截获的Message发送到CalculateService的监听地址:http://127.0.0.1:8888/interceptservice。

2、方案二

与方案一不同的是,CalculateService的EndpointAddress(逻辑地址)设成了InterceptService的地址:http://127.0.0.1:8888/interceptservice;而监听地址(物理地址)变成:http://127.0.0.1:9999/calculateservice。由于service的逻辑地址变成了http://127.0.0.1:8888/interceptservice,所以client端Endpoint的address也将变成该地址。很显然在这中情况下,client发送到service的message将实际发送给InterceptSerivce。InterceptSerivce对message处理完毕,把该message转发到CalculateService的监听地址:http://127.0.0.1:9999/calculateservice。

三、具体实现

1、方案一(Source Code从这里下载下载)

我们需要做的仅仅是改变CaculateService和InterceptService的配置文件:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <bindings>
   5:             <customBinding>
   6:                 <binding name="MyCustomeBinding">
   7:                     <textMessageEncoding />
   8:                     <httpTransport />
   9:                 </binding>
  10:             </customBinding>
  11:         </bindings>
  12:         <services>
  13:             <service name="Artech.MessageInterceptor.Services.CalculateService">
  14:                 <endpoint address="http://127.0.0.1:9999/calculateservice" binding="customBinding"
  15:                     bindingConfiguration="MyCustomeBinding" contract="Artech.MessageInterceptor.Contracts.ICalculate"
  16:                     listenUri="http://127.0.0.1:8888/Interceptservice" />
  17:             </service>
  18:         </services>
  19:     </system.serviceModel>
  20: </configuration>
  21:  
   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <bindings>
   5:             <customBinding>
   6:                 <binding name="MyCustomBinding">
   7:                     <textMessageEncoding />
   8:                     <httpTransport manualAddressing="true" />
   9:                 </binding>
  10:             </customBinding>
  11:         </bindings>
  12:         <client>
  13:             <endpoint address="http://127.0.0.1:8888/Interceptservice" binding="customBinding"
  14:                 bindingConfiguration="MyCustomBinding" contract="Artech.MessageInterceptor.Contracts.IIntercept"
  15:                 name="calculateService" />
  16:         </client>
  17:         <services>
  18:             <service name="Artech.MessageInterceptor.Services.InterceptService">
  19:                 <endpoint address="http://127.0.0.1:8888/Interceptservice" binding="customBinding"
  20:                     bindingConfiguration="MyCustomBinding" contract="Artech.MessageInterceptor.Contracts.IIntercept"
  21:                     listenUri="http://127.0.0.1:9999/calculateservice" />
  22:             </service>
  23:         </services>
  24:     </system.serviceModel>
  25: </configuration> 
  26:  

Manual Addressing

可能大家已经看到了,在custombing中的httpTransport
配置项中,将manualAddressing设置为true。manualAddressing在默认的情况下为false,意为着将按照WS-Addressing的方式进行Adressing。当该属性设为false,WCF会将client端的Endpoint的Address地址写入SOAP的To
header中,而将manualAddressing设为true,可以保留现有SOAP的To
header中的地址,在本例中InterceptService接收到的SOAP的To address为http://127.0.0.1:9999/calculateservice,然后在<client>中的endpoint address则是http://127.0.0.1:8888/Interceptservice。如果manualAddressing = false,那么To address将会变成http://127.0.0.1:8888/Interceptservice。当该SOAP抵达CalculateService时,由于ChannelDispatcher根据两个Message
Filter(Address Filter和Contract Filter)定位到对应的Endpoint。Address
Filter就是根据SOAP的To
address来进行筛选的,在默认的情况下,是找不到对应的Endpoint的。反之,我们manualAddressing=true,将保留SOAP的To
header中的address。

我们可以根据运行后的输出来验证这一点:

如何我们保留manualAddressing的默认值(false),那么为了让CalculateService的ChannelDispatcher能够有效地定位到对应的Endpoint,需要通过ServiceBehavior设置AddressFilterMode。(具体原理参考我的文章:WCF的中枢:Dispatcher

   1: using Artech.MessageInterceptor.Contracts;
   2: using System.ServiceModel;
   3: namespace Artech.MessageInterceptor.Services
   4: {
   5:     [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
   6:     public class CalculateService : ICalculate
   7:     {
   8:         #region ICalculate Members 
   9:  
  10:         public double Add(double x, double y)
  11:         {
  12:             return x + y;
  13:         } 
  14:  
  15:         #endregion
  16:     }
  17: }
  18:  

在这种情况下再次运行我们的程序,将会发现To Header中的Address变成了http://127.0.0.1:8888/Interceptservice

  2、方案二 (Source Code从这里下载)

我们看看方案二设计的配置的改动,首先是CalculateService:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <bindings>
   5:             <customBinding>
   6:                 <binding name="MyCustomeBinding">
   7:                     <textMessageEncoding/>
   8:                     <httpTransport />
   9:                 </binding>
  10:             </customBinding>
  11:         </bindings>
  12:         <services>
  13:             <service name="Artech.MessageInterceptor.Services.CalculateService">
  14:                 <endpoint address="http://127.0.0.1:8888/Interceptservice" binding="customBinding"
  15:                     bindingConfiguration="MyCustomeBinding" contract="Artech.MessageInterceptor.Contracts.ICalculate"
  16:                     listenUri="http://127.0.0.1:9999/calculateservice" />
  17:             </service>
  18:         </services>
  19:     </system.serviceModel>
  20: </configuration>
  21:  

接下来是InterceptService的配置:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <bindings>
   5:             <customBinding>
   6:                 <binding name="MyCustomBinding">
   7:                     <textMessageEncoding />
   8:                     <httpTransport manualAddressing="true" />
   9:                 </binding>
  10:             </customBinding>
  11:         </bindings>
  12:         <client>
  13:             <endpoint address="http://127.0.0.1:9999/calculateservice" binding="customBinding"
  14:                 bindingConfiguration="MyCustomBinding" contract="Artech.MessageInterceptor.Contracts.IIntercept"
  15:                 name="calculateService" />
  16:         </client>
  17:         <services>
  18:             <service name="Artech.MessageInterceptor.Services.InterceptService">
  19:                 <endpoint address="http://127.0.0.1:8888/Interceptservice" binding="customBinding"
  20:                     bindingConfiguration="MyCustomBinding" contract="Artech.MessageInterceptor.Contracts.IIntercept"/>
  21:             </service>
  22:         </services>
  23:     </system.serviceModel>
  24: </configuration> 
  25:  

最后是Client的

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>
   4:         <bindings>
   5:             <customBinding>
   6:                 <binding name="MyCustomBinding">
   7:                     <textMessageEncoding />
   8:                     <httpTransport />
   9:                 </binding>
  10:             </customBinding>
  11:         </bindings>
  12:         <client>
  13:             <endpoint name="calculateservice" address="http://127.0.0.1:8888/Interceptservice" binding="customBinding" bindingConfiguration="MyCustomBinding"
  14:                 contract="Artech.MessageInterceptor.Contracts.ICalculate" />
  15:         </client>
  16:     </system.serviceModel>
  17: </configuration>

WCF后续之旅: 
WCF后续之旅(1): WCF是如何通过Binding进行通信的 
WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel 
WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher 
WCF后续之旅(4):WCF Extension Point 概览 
WCF后续之旅(5): 通过WCF Extension实现Localization 
WCF后续之旅(6): 通过WCF Extension实现Context信息的传递 
WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成 
WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成 
WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I] 
WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II] 
WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance 
WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity) 
WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响 
WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇] 
WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇] 
WCF后续之旅(14):TCP端口共享 
WCF后续之旅(15): 逻辑地址和物理地址 
WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter) 
WCF后续之旅(17):通过tcpTracer进行消息的路由


作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-08-22 14:33:08

WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇]的相关文章

WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇]

WCF是.NET平台下实现SOA的一种手段,SOA的一个重要的特征就基于Message的通信方式.从Messaging的角度讲,WCF可以看成是对Message进行发送.传递.接收.基础的工具.对于一个消息交换的过程,很多人只会关注message的最初的发送端和最终的接收端.实际上在很多情况下,在两者之间还存在很多的中间结点(Intermediary),这些中间结点在可能在实际的应用中发挥中重要的作用.比如,我们可以创建路由器(Router)进行消息的转发,甚至是Load Balance:可以创

WCF后续之旅(13) 创建一个简单的WCF SOAP Message拦截、转发工具

WCF是.NET平台下实现SOA的一种手段,SOA的一个重要的特征就基于Message的通信方式.从Messaging的角度讲,WCF可以看成是对Message进行发送.传递.接收.基础的工具.对于一个消息交换的过程,很多人只会关注message的最初的发送端和最终的接收端.实际上在很多情况下,在两者之间还存在很多的中间结点(Intermediary),这些中间结点在可能在实际的应用中发挥中重要的作用.比如,我们可以创建路由器(Router)进行消息的转发,甚至是Load Balance:可以创

WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成

松耦合.高内聚是我们进行设计的永恒的目标,如何实现这样的目标呢?我们有很多实现的方式和方法,不管这些方式和方法在表现形式上有什么不同,他们的思想都可以表示为:根据稳定性进行关注点的分离或者分解,交互双方依赖于一个稳定的契约,而降低对对方非稳定性因素的依赖.从抽象和稳定性的关系来讲,抽象的程度和稳定程度成正相关关系.由此才有了我们面向抽象编程的说法,所以"只有依赖于不变,才能应万变". 然后,对于面向对象的思想来讲,我们的功能通过一个个具体的对象来承载.对象是具体的,不是抽象的:创建对象

WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成

在上一篇文章中,我们通过自定义InstanceProvider实现了WCF和微软Enterprise Library Unity Application Block的集成, 今天我们已相同的方式实现WCF与Enterprise Library的另一个Application Block的集成:Policy Injection Application Block (PIAB). PIAB,通过Method Interception的机制实现了AOP(Aspect Oriented Programin

《WCF后续之旅》博文系列总结[共17篇]

<我的WCF之旅>系列自开篇以来,得到了园子里很多朋友的厚爱,并荣登了博客园2007年度系列博文Top 10.由于工作原因,沉寂了一阵,两个月前开始WCF新的旅程.如果说<我的WCF之旅>主要是对WCF基本原理概括性介绍,而对于这个新的系列,我将和大家分享我对WCF的一些实现机制.设计原理的理解,以及我在实际的项目开发中的一些实践经验(比如在后续的一些文章中,我将介绍通过WCF Extension实现一些在真正的分布式项目开发中很有现实意义的功能). [第1篇] WCF是如何通过B

WCF后续之旅(9): 通过WCF双向通信实现Session管理[下篇]

一.Session Management Service的实现 现在我们来看看Session Management真正的实现,和我以前的例子不同,我不是把所有的实现都写在WCF service上,而是定义了另一个class来实现所有的业务逻辑:SessionManager.我们分析一下具体的实现逻辑. 1: namespace Artech.SessionManagement.Service 2: { 3: public static class SessionManager 4: { 5: p

WCF后续之旅(4):WCF Extension Point 概览

在本系列的每篇文章中,我多次提到WCF是一个极具可扩展性的分布是消息通信框架.为了让读者对WCF Extension有一个总体的的认识,在这里我会简单列举了我们经常使用的绝大部分的扩展点,以及通过这些扩展点能够解决实现项目开发中的那些问题. 有一点需要特别提醒的是:对WCF extensions的灵活应用依赖于你对channel layer和service mode dispatching system的深入理解.所以,如果你对channel layer不甚了解,可以参阅本系列的第一个部分(WC

WCF后续之旅(9):通过WCF的双向通信实现Session管理[上篇]

我们都知道,WCF支持Duplex的消息交换模式,它允许在service的执行过程中实现对client的回调.WCF这种双向通信的方式是我们可以以Event Broker或者订阅/发布的方式来定义和调用WCF Service.今天我们就给大家一个具体的例子:通过WCF的duplex communication方式现在Session管理. 一.Session 管理提供的具体功能 我们的例子实现了下面一些Session Management相关的功能: Start/End Session:可以调用s

[原创]WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响

在本系列的上一篇文章中,我们重点讨论了线程关联性对service和callback的操作执行的影响:在service host的时候,可以设置当前线程的SynchronizationContext,那么在默认情况下,service操作的执行将在该SynchronizationContext下执行(也就将service操作包装成delegate传入SynchronizationContext的Send或者Post方法):同理,对于Duplex同行方式来讲,在client调用service之前,如果