WCF Service端Inspector
问题
在使用WCF的过程中,有时候需要在service端截取client和service之间的消息来做一些如写log,检查message是否合法的操作。 那么如何才能实现呢?
解决方案
使用WCF提供的Inspector功能。我们可以通过实现WCF提供的IParameterInspector或者IDispatchMessageInspector 接口来实现上述需求。以下是需要实现步骤:
1. 实现IParameterInspector或者IDispatchMessageInspector接口
2. 实现IServiceBehavior/IEndpointBehavior/IOperationBehavior接口并把步骤1中实现的Inspector加入到WCF的Message dispatch runtime中
3. 通过Attribute或者配置文件把步骤2中的Behavior应用到WCF service上
接下来我们看看每一步如何实践:
- 步骤一 --- 实现IParameterInspector或者IDispatchMessageInspector接口
实现IParameterInspector的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class LogParameterInspector : IParameterInspector { public void AfterCall( string operationName, object [] outputs, object returnValue, object correlationState) { } public object BeforeCall( string operationName, object [] inputs) { var cinfo = new LogInfo(); cinfo.Action = operationName; cinfo.StartTimeStamp = DateTime.Now; cinfo.ServiceName = OperationContext.Current.InstanceContext.GetServiceInstance().GetType().Name; return cinfo; } } |
实现IDispatchMessageInspector的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | { public object AfterReceiveRequest( ref Message request, IClientChannel channel, InstanceContext instanceContext) { LogInfo cinfo = new LogInfo(); //Collect any info that passed in the headers from Client, if any. cinfo.ServerTimeStamp = DateTime.Now; //Timestamp after the call is received. cinfo.Platform = "WCF" ; OperationDescription operationDesc = GetOperationDescription(OperationContext.Current); if (operationDesc != null ) { Type contractType = operationDesc.DeclaringContract.ContractType; cinfo.Action = operationDesc.Name; cinfo.ServiceName = contractType.FullName; cinfo.AssemblyName = contractType.Assembly.GetName().Name; } cinfo.ServerName = Dns.GetHostName(); cinfo.ServerProcessName = System.AppDomain.CurrentDomain.FriendlyName; return cinfo; } public void BeforeSendReply( ref Message reply, object correlationState) { //correlationState is of type ClientInformation and it is set in AfterReceiveRequest event. if (correlationState != null ) { LogInfo cinfo = correlationState as LogInfo; if (cinfo != null ) { cinfo.EndTimeStamp = DateTime.Now; //It's okay to read the RequestMessage since the operation //has been completed and message is serialized from stream //(....stream...). //RequestMessage on OperationContext is short lived. //It would be too late to serialize the RequestMessage //in another thread (exception raised: Message is closed) cinfo.Request = OperationContext.Current.RequestContext.RequestMessage.ToString(); //Message can be read only once. //Create a BufferedCopy of the Message and be sure to set //the original message set to a value wich has not been //copied nor read. MessageBuffer mb = reply.CreateBufferedCopy( int .MaxValue); Message responseMsg = mb.CreateMessage(); reply = mb.CreateMessage(); var reader = responseMsg.GetReaderAtBodyContents(); var xodc = new XmlDocument(); xodc.LoadXml(reader.ReadOuterXml()); var oo = reader.ReadContentAsObject(); cinfo.Response = responseMsg.ToString(); if (reply.IsFault == true ) { cinfo.IsError = true ; } //Log cinfo async here; var serialzer = new XmlSerializer(cinfo.GetType()); var writer = new StringWriter(); serialzer.Serialize(writer, cinfo); File.WriteAllText( @"c:\log.xml" , writer.ToString()); } } } private OperationDescription GetOperationDescription(OperationContext operationContext) { OperationDescription od = null ; string bindingName = operationContext.EndpointDispatcher.ChannelDispatcher.BindingName; string methodName; if (bindingName.Contains( "WebHttpBinding" )) { //REST request methodName = ( string )operationContext.IncomingMessageProperties[ "HttpOperationName" ]; } else { //SOAP request string action = operationContext.IncomingMessageHeaders.Action; methodName = operationContext.EndpointDispatcher.DispatchRuntime.Operations.FirstOrDefault(o => o.Action == action).Name; } EndpointAddress epa = operationContext.EndpointDispatcher.EndpointAddress; ServiceDescription hostDesc = operationContext.Host.Description; ServiceEndpoint ep = hostDesc.Endpoints.Find(epa.Uri); if (ep != null ) { od = ep.Contract.Operations.Find(methodName); } return od; } } |
- 步骤二 --- 实现IServiceBehavior或者IEndpointBehavior或者IOperationBehavior接口中的一个,以下以实现IServiceBehavior接口为例
实现IServiceBehavior的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public class ServiceBehavior : Attribute, IServiceBehavior { public ServiceBehavior() { } public void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers) { ChannelDispatcher cd = cdb as ChannelDispatcher; if (cd != null ) { foreach (EndpointDispatcher ed in cd.Endpoints) { if (!ed.DispatchRuntime.MessageInspectors.Any(inspector => inspector is LogDispatchMessageInspector)) { ed.DispatchRuntime.MessageInspectors.Add( new LogDispatchMessageInspector()); } foreach (DispatchOperation op in ed.DispatchRuntime.Operations) { if (!op.ParameterInspectors.Any(inspector => inspector is LogParameterInspector)) { op.ParameterInspectors.Add( new LogParameterInspector()); } } } } } } public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { } } |
- 步骤三 --- 通过Attribute或者配置文件把步骤2中的Behavior应用到WCF service上
我们有如下两种方式把步骤二中实现的Behavior应用到具体的Service上:
1) 让Behavior类继承Attribute,然后把Behavior作为Attribute应用到具体的Service上,如前所示,步骤二中的Behavior已经继承Attribute了,
所以我们可以像下面这样把它应用到具体的service上:
1 2 | [LogInspector.ServiceBehavior] public class Service1 : IService1 |
2) 通过配置文件
编写ServiceBehaviorExtensionElement并继承自BehaviorExtensionElement class,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | public class ServiceBehaviorExtensionElement : BehaviorExtensionElement { protected override object CreateBehavior() { return new ServiceBehavior(); } public override Type BehaviorType { get { return typeof (ServiceBehavior); } } } |
然后在web.config文件中做如下配置:
<system.serviceModel> <extensions> <behaviorExtensions> <add name="LogInspectorExtension" type="LogInspector.ServiceBehaviorExtensionElement, LogInspector"/> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior name="LogInpsectorBehavior"> <LogInspectorExtension></LogInspectorExtension> <!-- To avoid disclosing metadata information, set the values below to false before deployment --> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="LogService.Service1" behaviorConfiguration="LogInpsectorBehavior"> <endpoint address="" contract="LogService.IService1" binding="wsHttpBinding"></endpoint> </service> </services> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>
本文代码下载地址:https://github.com/DerekLoveCC/WCF.git
https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/18/wcf-extensibility-message-inspectors/
https://code.msdn.microsoft.com/Generic-WCF-Message-bdf4fb1f
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
2014-03-04 ‘Microsoft.Jet.OLEDB.4.0’ provider is not registered