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
WCF Service端Inspector的更多相关文章
- 如何创建一个AJAX-Enabled WCF Service
原创地址:http://www.cnblogs.com/jfzhu/p/4041638.html 转载请注明出处 前面的文章中介绍过<Step by Step 创建一个WCF Servi ...
- 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 ...
- WCF Service Configuration Editor的使用
原文:http://www.cnblogs.com/Ming8006/p/3772221.html 通过WCF Service Configuration Editor的配置修改Client端 参考 ...
- WCF 服务端异常封装
通常WCF服务端异常的详细信息只有在调试环境下才暴露出来,但我目前有需求需要将一部分异常的详细信息传递到客户端,又需要保证一定的安全性. 最简单的办法当然是在服务端将异常捕获后,序列化传给客户端,但这 ...
- 探讨 : Host在IIS上的WCF Service的执行方式
一个WCF请求由两个线程来完成 运行在IIS上的WCF service, 你可能会注意到一个比较有趣的现象. 当WCF service接收到一个请求时, 这个请求实际上会有两个线程在执行这个请求. 一 ...
- WCF服务端开发和客户端引用小结
1.服务端开发 1.1 WCF服务创建方式 创建一个WCF服务,总是会创建一个服务接口和一个服务接口实现.通常根据服务宿主的不同,有两种创建方式. (1)创建WCF应用程序 通过创建WCF服务应用程序 ...
- 如何创建一个RESTful WCF Service
原创地址:http://www.cnblogs.com/jfzhu/p/4044813.html 转载请注明出处 (一)web.config文件 要创建REST WCF Service,endpoin ...
- 用JavaScript调用WCF Service
原创地址:http://www.cnblogs.com/jfzhu/p/4039604.html 转载请注明出处 前面介绍过<Step by Step 创建一个WCF Service>和& ...
- Step by Step 创建一个WCF Service
原创地址:http://www.cnblogs.com/jfzhu/p/4025448.html 转载请注明出处 (一)创建WCF Service (1)创建WCF Service类库 创建一个Cla ...
随机推荐
- HDU 2896 病毒侵袭 (AC自动机)
这题模板题.............但是竟然要去重........调试了半天才发现.................... #include <cstdio> #include <i ...
- [LinqPad妙用]-在Net MVC中反射调用LinqPad中的Dump函数
LinqPad有个非常强大的Dump函数.这篇讲解一下如何将Dump函数应用在.Net MVC Web开发中. 先看效果: 一.用.Net Reflector反编译LinqPad.exe,找出Dump ...
- jQuery插件autoComplete使用
安装/需要引入的文件 <script type="text/javascript" src="../js/jquery-1.8.3.min.js.js"& ...
- Java并发编程--线程封闭(Ad-hoc封闭 栈封闭 ThreadLocal)
线程封闭实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢?就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程 ...
- 手把手教你发布代码到CocoaPods(Trunk方式)-备用
概述 关于CocoaPods的介绍不在本文的主题范围内,如果你是iOS开发者却不知道CocoaPods,那可能要面壁30秒了.直奔主题,这篇文章主要介绍如果把你的代码发布到CocoaPods代码库中, ...
- CSS3 Flexbox布局那些事
相信研究过CSS3的同学对Flexbox布局一定不会陌生(作为一个未来主流的布局方式,至少有所耳闻).最近完成了两个项目:一个是移动端H5项目,一个是嵌入HTML页面的mac客户端项目.为了庆祝这两个 ...
- performance
简介 延缓执行 JavaScript 是一个能有效提高网页加载速度以及提升用户阅读体验质量的途径.从实际经验来看,将我们的网站从经济实惠的 VPS 迁移到 Softlayer(美国著名的一个数据中心) ...
- python中的嵌套类(内部类调用外部类中的方法函数)
在为书中版本是3.X的,但2.X不太支持直接调用. 所以,在PYTHON2.X中,要在内部类中调用外部类的方法,就必须得实例化外部类,然后,传入实例进行调用. 花了我两个小时啊,资料没找到,自己一个一 ...
- Codeforces 159D Palindrome pairs
http://codeforces.com/problemset/problem/159/D 题目大意: 给出一个字符串,求取这个字符串中互相不覆盖的两个回文子串的对数. 思路:num[i]代表左端点 ...
- 自制单片机之七……扩展:DS18B20温度测量
DS18B20数字温度测量传感器,网上介绍很多,我就不罗嗦了.见图 DS18B20与前产品DS1820的不同: DS18B20继承了DS1820的全部优点,并做了如下改进 1.供电范围扩大为3.0-- ...