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

posted @   Fintech技术汇  阅读(641)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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
点击右上角即可分享
微信分享提示