问题

  在使用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. smarty练习: 设置试题及打印试卷

    数据库表格:shiti, shititimu, timu, kemu, xuanxiang 根据科目设置一个可以添加试题的页面:(如下图) 具体的题目从数据库中查出并形成一张试卷的形式 考试试题设置: ...

  2. pfsense 2.2RC版本应用

    为什么要上RC版本呢?因为华硕主板有一个RTL8111G驱动在2.15中还没有识别.... 公司双线WAN,一个PPPOE一个静态IP. 开了端口转发, 要求对不同的IP进行相关限速, 到达指定网站用 ...

  3. 技巧两种:LINUX删除指定后缀文件及PYTHON更改屏幕字色

    http://blog.csdn.net/caryaliu/article/details/8753028 http://www.iitshare.com/python-print-color-log ...

  4. MVC每层的职责

    MVC模式把应用程序分割成三层:模型.视图和控制器. 模型:模型代表着核心的业务逻辑和数据.模型封装了域实体的属性和行为,并暴露出了实体的属性. 视图: 视图负责转换模型并把它传递给表示层.视图应关注 ...

  5. About Undefined Behavior[译文]

    原文:blog.llvm.org/2011/05/what-every-c-programmer-should-know.html 人们偶尔会问为什么LLVM的汇编代码有时会在优化器打开时产生SIGT ...

  6. Linux下查看文件夹或目录大小

    当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择. df可以查看一级文件夹大小.使用比例.档案系统及其挂入点,但对文件却无能为力.    du可以查看文件及文件夹的大小.d ...

  7. 【转】ubuntu14.04 trusty的源

    原文网址:http://blog.chinaunix.net/uid-15041-id-4821715.html 一.编辑更新源文件:/etc/apt/sources.list二.更新源索引文件:ap ...

  8. MVC4.0系统开发新手历程(一)

    接手了一个简单的销售奖金计算的项目,虽然不算大但是业务逻辑比较复杂,还夹杂了很多的特殊情况,毕竟是大公司什么样的人都有,好了不多说切入正题,项目是公司的一个前辈负责的,在他做系统架构的时候让我们了解下 ...

  9. jQuery 各种选择器 $.()用法

    jQuery 元素选择器jQuery 使用 CSS 选择器来选取 HTML 元素. $("p") 选取 <p> 元素. $("p.intro") 选 ...

  10. Windows系统结构

    四种用户模式进程:1.系统支持进程,比如登录进程和会话管理器,并不是Windows服务,不有服务控制管理器启动2.服务进程,一些以Windows服务方式来运行的组件3.用户应用进程4.环境子系统服务器 ...