在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)
二、实现原理解析
不同于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>
作者:Artech 出处:http://artech.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。