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 ...
随机推荐
- SignalR2.0开发实例之——设置时间、后台其他地方使用集线器、使用自己的连接ID
一.连接的生命周期设置: 如下: // 该值表示连接在超时之前保持打开状态的时间长度. //默认为110秒 GlobalHost.Configuration.ConnectionTimeout = T ...
- HTML&CSS基础学习笔记1.9-添加图片
<img>标签是用来添加图片的~ <img>标签的使用方法:<img src="图片的地址"> 先来看段实例代码: <!DOCTYPE h ...
- C语言初学 计算二元一次方程的问题
#include<stdio.h> #include<math.h> int main() { double a,b,c,disc,x1,x2; scanf("%lf ...
- 使用redis缓存加索引处理数据库百万级并发
使用redis缓存加索引处理数据库百万级并发 前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1 ...
- Kafka笔记--使用ubuntu为bocker(服务器)windows做producer和comsumer(客户端)
原文连接:http://www.cnblogs.com/davidwang456/p/4201875.html 程序仍然使用之前的一篇博文中的例子 :http://www.cnblogs.com/gn ...
- mysql日志文件相关的配置【2】
1.二进制日志是什么? mysql 的二进制日志用于记录数据库上做的变更. 2.二进制日志什么时间写到磁盘 1.总的来说二进制日志会在释放锁之前就写入磁盘.也就是说在commit完成之前:client ...
- Unity GUI 用C#和Javascript写法的区别
以前都是用C#来写Unity的GUI.后来因为团队需要GUI必须用C#写. 其实一开始学Unity的GUI的时候我是想用C#来写,后来折腾了好久也没弄出来.反倒是这次不经意间就搞好了. C#和Java ...
- 关于popupwindow的两种实现方式
http://104zz.iteye.com/blog/1685389 android PopupWindow实现从底部弹出或滑出选择菜单或窗口 本实例弹出窗口主要是继承PopupWindow类来实现 ...
- keil Ax51中条件编译指令IF与$IF的区别
keil A51中条件编译指令IF与$IF的区别:1.IF和$IF是不等价的,不要混淆了;2.带前缀$的条件编译$IF用法:(汇编器指示命令Assembler Directive)只能用来测试由$SE ...
- 单例模式 - OK
单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点. 一.单例模式 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象.一个最好的办法就是,让 ...