• 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的类
      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的类

      {
            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的类

    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上:
       [LogInspector.ServiceBehavior]
        public class Service1 : IService1  
    

    2) 通过配置文件
    编写ServiceBehaviorExtensionElement并继承自BehaviorExtensionElement class,代码如下:

    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

  • 相关阅读:
    Luogu_P3435 [POI2006]OKR-Periods of Words KMP
    NOIP2019游记
    []記錄容易出錯的地方和一些知識
    [题解]luogu_P3939_数颜色(vector二分
    [题解]luogu_P4819_杀人游戏(缩点
    [题解]NOIP2018_赛道修建(二分/树形dp/set/贪心
    [题解]luogu_P4161_(排列/lcm
    [题解]luogu_P4886_快递员(点分治
    [题解]luogu_P3523(树上覆盖
    [题解]luogu_P2466(区间dp
  • 原文地址:https://www.cnblogs.com/dereklovecc/p/5243117.html
Copyright © 2020-2023  润新知