问题

  在使用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. (四)backbone - DEMO - 通信录

    DEMO介绍 是DEMO - User List 的扩展,增加查询 大体实现 •创建Contact Model var Contact = Backbone.Model.extend({ defaul ...

  2. 武汉科技大学ACM :1008: 零起点学算法58——开灯问题

    Problem Description 计算中心有8个机房,每个机房有n台电脑.每台电脑都有一个编号,比如8号机房编号就为H1到Hn,我们有时又称为H1为1号机器,H2为2号机器,.... 有一天我们 ...

  3. css3之background

    background background: (1)url(image1.png) right bottom, (2)url(image2.png) center, (3)url(image3.png ...

  4. JavaScript高级程序设计第20章JSON 笔记 (学习笔记)

    第二十章 JSON 1.Json 可以表示三种类型的值: 1.简单值: 表示数值:5  表示字符串:“hello wrold”注表示字符串时必须使用双引号 2.对象: {“name”:“mi”,”ag ...

  5. Linux修改时间时区并在Tomcat中生效

    Linux查看当前时间时区linux:~ # datelinux:~ # date –Rlinux:~ # zdump -v /usr/share/zoneinfo/Asia/Beijing ---- ...

  6. CSS3 @font-face (webfont)

    先大概介绍下计算机领域常见的字体类型与格式. 点阵字体(Bitmap Font)点阵字体也叫位图字体,其中每个字形都以一组二维像素信息表示.这种文字显示方式于较早前的电脑系统(例如未有图形接口时的 D ...

  7. [转载]css3属性box-sizing:border-box的作用

    http://jastate.com/css3-properties-box-sizingborder-box.html 定义和用法 按照w3c school的说法,box-sizing:border ...

  8. Feedly使用技巧

    1:用Chrome的话推荐这个应用:RSS Subscription Extension下载地址:https://chrome.google.com/webstore/detail/rss-subsc ...

  9. ThrottleStop

    ThrottleStop 我的要开这个软件,睿频才能开.不然一直工作在1.8Ghz下默认频率太低了开了这个速度才有提升

  10. C++中,如何在标准库的std::string和常用库(Qt,VC等)的QString之间进行选择?

    假设一个场景:在写GUI程序的时候,如果GUI库和STL都提供了某个功能(比如容器字符串),应该如何在两个库之间选择? 做法是分层,比如分为frontend+core.开发core的时候只用STL,保 ...