问题

  在使用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

WCF Service端Inspector的更多相关文章

  1. 如何创建一个AJAX-Enabled WCF Service

      原创地址:http://www.cnblogs.com/jfzhu/p/4041638.html 转载请注明出处   前面的文章中介绍过<Step by Step 创建一个WCF Servi ...

  2. If WCF Service side and Client side config is different?!

    from stackoverflow http://stackoverflow.com/questions/4879310/when-setting-up-a-wcf-client-and-serve ...

  3. WCF Service Configuration Editor的使用

    原文:http://www.cnblogs.com/Ming8006/p/3772221.html 通过WCF Service Configuration Editor的配置修改Client端 参考 ...

  4. WCF 服务端异常封装

    通常WCF服务端异常的详细信息只有在调试环境下才暴露出来,但我目前有需求需要将一部分异常的详细信息传递到客户端,又需要保证一定的安全性. 最简单的办法当然是在服务端将异常捕获后,序列化传给客户端,但这 ...

  5. 探讨 : Host在IIS上的WCF Service的执行方式

    一个WCF请求由两个线程来完成 运行在IIS上的WCF service, 你可能会注意到一个比较有趣的现象. 当WCF service接收到一个请求时, 这个请求实际上会有两个线程在执行这个请求. 一 ...

  6. WCF服务端开发和客户端引用小结

    1.服务端开发 1.1 WCF服务创建方式 创建一个WCF服务,总是会创建一个服务接口和一个服务接口实现.通常根据服务宿主的不同,有两种创建方式. (1)创建WCF应用程序 通过创建WCF服务应用程序 ...

  7. 如何创建一个RESTful WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4044813.html 转载请注明出处 (一)web.config文件 要创建REST WCF Service,endpoin ...

  8. 用JavaScript调用WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4039604.html 转载请注明出处 前面介绍过<Step by Step 创建一个WCF Service>和& ...

  9. Step by Step 创建一个WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4025448.html 转载请注明出处 (一)创建WCF Service (1)创建WCF Service类库 创建一个Cla ...

随机推荐

  1. Comparable与compareTo

    Comparable 1.什么是Comparable接口 此接口强行对实现它的每个类的对象进行整体排序.此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它的自然比较方法 .实现此接口 ...

  2. 【转】C++成员函数的存储方式

    [转] http://c.biancheng.net/cpp/biancheng/view/187.html 用类去定义对象时,系统会为每一个对象分配存储空间.如果一个类包括了数据和函数,要分别为数据 ...

  3. 字符串编码---hash函数的应用

    之前就听说过有个叫做hash表的东西,这段时间在上信息论与编码,也接触了一些关于编码的概念,直到今天做百度之星的初赛的d题时,才第一次开始学并用hash 一开始我用的是mutimap和mutiset, ...

  4. MySQL学习笔记(5) - 修改和删除数据库

    1.完整语句 ALTER {DATABASE | SCHEMA} [db_name] [DEFAULT] CHARACHER SET [=] charset_name; 2.修改数据库的编码方式 al ...

  5. tomcat 显示目录文件列表

    conf/web.xml中,listings改为true,重启 http://liusu.iteye.com/blog/794613 <servlet> <servlet-name& ...

  6. ios开发之常用宏的定义

    有些时候,我们需要将代码简洁化,这样便于读代码.我们可以将一些不变的东东抽取出来,将变化的东西作为参数.定义为宏,这样在写的时候就简单多了. 下面例举了一些常用的宏定义和大家分享: 1. 判断设备的操 ...

  7. Java学习笔记--NIO

    参考资料:http://ifeve.com/buffers/ BIO/NIO/AIO的区别联系 http://stevex.blog.51cto.com/4300375/1284437http://w ...

  8. TextField控件详解2

    //初始化textfield并设置位置及大小 UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 130, ...

  9. Matlab 图像预处理

    %%%%%%%%%%%%%%%%% %%降采样 clear all im={}; %创建字典保存读取的图片 dis=dir('F:\kaggle_data_zip\Sample\*.jpeg');%% ...

  10. SQL Fetch size

    JDBC performance tuning with optimal fetch size February 1, 2009 31 Comments Tuning performance usin ...