上文中谈到了WCF截获消息有四种方式1) 路由法 2) 自定义绑定法 3)实现接口法 4) 跟踪诊断法,上篇文章总结WCF中截获消息的几种方式详细的阐述了前面两种,本文着重阐述后面两种实现方式
3. 接口实现法
WCF已经考虑到了开发人员想通过自定义代码截获消息的需求,在WCF中,Binding,Contract,Address是构成了EndPoint,他们是Infrastructure.而Channel是Communication的最关键部分,而Behavior则是充分体现WCF扩展灵活性的一个点,如果将上面的Endpoint与Channel是树干,那么Behavior就是树枝。树干组成了参天大树,而树枝则是对树干的点缀。如果想成为一个健壮的大树,树干必不可少,而树叶可多可少,可有可无,但树叶能影响整个大树的外观特征。在WCF中自定义了三种Behavior,他们分别是:
IEndpointBehavior
IServiceBehavior
IOperationBehavior
三种Behavior作用对象分别是:Endpoint,Service,Operation.下面看下IEndpointBehavior的定义
public interface IEndpointBehavior { // Methods void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection
bindingParameters); void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime); void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher); void Validate(ServiceEndpoint endpoint); }
本文中注重讨论ApplyClientBehavior和ApplyDispatchBehavior,其中ApplyClientBehavior是用于应用客户端的行为,而ApplyDispatchBehavior用于服务端行为。在ApplyClientBehavior方法中的参数ClientRuntime是扩展客户端的最重要的类,他是WCF框架提供给开发人员插入扩展客户端处理消息功能的插入点。ClientRuntime类中有一个MessageInspectors属性成员,它是客户端消息检查器的集合,WCF为客户端消息检查器提供了标准接口:IClientMessageInspector,它的定义是:
public interface IClientMessageInspector { // Methods void AfterReceiveReply(ref Message reply, object correlationState); object BeforeSendRequest(ref Message request, IClientChannel channel); }
在消息交互过程中,客户端和服务端交互的过程如下图所示:
从上图中我们可以看出,消息在客户端有两种:1)在发送消息之前 2) 接收到消息之后 这两个分别对应
IClientMessageInspector的两个接口方法BeforeSendRequest AfterReceiveReply。
在ApplyDispatchBehavior的方法中的参数EndpointDispatcher中有一个属性:DispatchRuntime和
ClientRuntime相对应,它也有一个MessageInspectors属性,它是服务端消息检查器的集合,WCF为服务端消息检查器
提供了一个标准接口:IDispatchMessageInspector,他的定义是:
public interface IDispatchMessageInspector { // Methods object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext
instanceContext); void BeforeSendReply(ref Message reply, object correlationState); }
它的两个方法对应这上图中服务中的两种消息,一种是接收请求后,另外一种是发送响应前。
通过实现IClientMessageInspector IDispatchMessageInspector 这两个接口,就能实现客户端和服务端的消息拦截
器,再通过实现自定义的Behavior,然后将自定义的Behavior添加到相应的行为限制对象上,如Endpoint,Service,
Operation上就能实现对消息的拦截。做了个demo,想查看详细代码的可以下载。
这里只给出最后的运行效果图:
服务端:
客户端:
采用这种方法的优点是:
1) 能适用于各种场合,比如使用诸如 HTTPS 的安全传输时。
缺点是实现比较复杂,且不能跟踪传输级别的消息
4. 跟踪诊断法
WCF本身也提供了管理诊断功能,由于种种原因,我们可能在设计时可能没有想到记录消息的需求,而在实际运维的时候却出
现这样的需求,因为有些问题issue的解决需要查看消息内容才能更容易的诊断。WCF也提供了缺省的功能用于满足上述需求
默认情况系,WCF不记录Message,但可以通过配置config,能实现消息记录,主要是通过
System.ServiceModel.MessageLogging。下面的代码既可以增加消息记录功能
<system.diagnostics> <sources> <source name="System.ServiceModel.MessageLogging"> <listeners> <add name="messages" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\logs\messages.svclog" /> </listeners> </source> </sources> </system.diagnostics> <system.serviceModel> <diagnostics> <messageLogging logEntireMessage="true" logMalformedMessages="false" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000"/> </diagnostics> </system.serviceModel>
简要的介绍一下上面的代码,System.Diagnostics.XmlWriterTraceListener是.net表转的跟踪侦听器,使用它,必须
通过指定initializeData来设置记录的消息保存的文件地址。
在WCF中消息记录是分级别的,基本划分为:”服务“级别,”传输“级别,”格式不正确“级别。有关这三个级别,msdn
有详细的描述,我就不再赘述。
通过此种方法,还可以通过添加消息筛选器,实现对部分的消息内容进行记录,下面的代码
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="420"> <filters> <add nodeQuota="10" xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> /soap:Envelope/soap:Header </add> </filters> </messageLogging>
这段代码便是指定了只记录消息标头,这些个内容,msdn上都有,不多说了。
也做了一个诊断跟踪的示例,执行后在c盘下能找到一个messages.txt的文件。内容是:
<E2ETraceEvent xmlns=" http://schemas.microsoft.com/2004/06/E2ETraceEvent%22><System xmlns=" http://schemas.microsoft.com/2004/06/windows/eventlog/system%22><EventID>0</EventID><Type>3</Type><SubType Name="Information">0</SubType><Level>8</Level><TimeCreated SystemTime="2008-10-25T14:46:58.1093176Z" /><Source Name="System.ServiceModel.MessageLogging" /><Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" /><Execution ProcessName="Jillzhang.Wcf.MessageInspectors.Trace.Host.vshost" ProcessID="4556" ThreadID="11" /><Channel/><Computer>WIN-QMIXGNBHWPE</Computer></System><ApplicationData><TraceData><DataItem><MessageLogTraceRecord Time="2008-10-25T22:46:58.1043176+08:00" Source="ServiceLevelReceiveRequest" Type="System.ServiceModel.Channels.BufferedMessage" xmlns=" http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace%22><s:Envelope xmlns:s=" http://www.w3.org/2003/05/soap-envelope%22 xmlns:a=" http://www.w3.org/2005/08/addressing%22><s:Header><a:Action s:mustUnderstand="1"> http://tempuri.org/ICalculator/Add</a:Action><a:MessageID>urn:uuid:1ef15f49-6e38-46d3-b906-d8ed78564c89</a:MessageID><a:ReplyTo><a:Address> http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><VsDebuggerCausalityData xmlns=" http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink%22>uIDPo8masvhLEkZBvyYJpVFd168AAAAAgVozn8DAXkaGix0+tb393XpBqSmzHQxOuVyNwKQn8egACQAA</VsDebuggerCausalityData><a:To s:mustUnderstand="1">net.tcp://127.0.0.1:8023/Calculator</a:To></s:Header><s:Body><Add xmlns=" http://tempuri.org/%22><a>1</a><b>2</b></Add></s:Body></s:Envelope></MessageLogTraceRecord></DataItem></TraceData></ApplicationData></E2ETraceEvent><E2ETraceEvent xmlns=" http://schemas.microsoft.com/2004/06/E2ETraceEvent%22><System xmlns=" http://schemas.microsoft.com/2004/06/windows/eventlog/system%22><EventID>0</EventID><Type>3</Type><SubType Name="Information">0</SubType><Level>8</Level><TimeCreated SystemTime="2008-10-25T14:46:58.1293176Z" /><Source Name="System.ServiceModel.MessageLogging" /><Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" /><Execution ProcessName="Jillzhang.Wcf.MessageInspectors.Trace.Host.vshost" ProcessID="4556" ThreadID="11" /><Channel/><Computer>WIN-QMIXGNBHWPE</Computer></System><ApplicationData><TraceData><DataItem><MessageLogTraceRecord Time="2008-10-25T22:46:58.1293176+08:00" Source="ServiceLevelSendReply" Type="System.ServiceModel.Channels.BodyWriterMessage" xmlns=" http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace%22><s:Envelope xmlns:a=" http://www.w3.org/2005/08/addressing%22 xmlns:s=" http://www.w3.org/2003/05/soap-envelope%22><s:Header><a:Action s:mustUnderstand="1"> http://tempuri.org/ICalculator/AddResponse</a:Action></s:Header><s:Body><AddResponse xmlns=" http://tempuri.org/%22><AddResult>3</AddResult></AddResponse></s:Body></s:Envelope></MessageLogTraceRecord></DataItem></TraceData></ApplicationData></E2ETraceEvent>
诊断跟踪的方式非常灵活,且获取的消息也非常详细,非常适用于生产环境。同时支持自定义诊断跟踪器。推荐使用
完整的dmeo示例:http://files.cnblogs.com/jillzhang/Jillzhang.Wcf.MessageInspector2.rar