从控制台应用程序记录SOAP消息

我正在尝试将我开发的控制台应用程序和特定的第三方远程SOAP Web服务之间的请求和响应(原始XML SOAP信封)记录到数据库以进行审计,我找不到办法。

理想情况下,我想做的是获取请求

    Albireo    

和响应

    Hello, Albireo.    

并将它们保存在数据库中。

到目前为止,我发现网上的每个教程都归结为两种方法,即SoapExtension方法和跟踪方法。

SoapExtension方法

SoapExtension方法基于SOAP Message Modification Using SOAP Extensions指南,在此方法中,您创建一个inheritance自SoapExtension的类并将其挂钩到应用程序的配置中,类的ProcessMessage方法将允许您拦截SOAP消息。

这是从SoapExtensioninheritance的类的示例:

 namespace Playground.Client { using System; using System.Web.Services.Protocols; public class SoapLogger : SoapExtension { public override object GetInitializer(System.Type serviceType) { throw new NotImplementedException(); } public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { throw new NotImplementedException(); } public override void Initialize(object initializer) { throw new NotImplementedException(); } public override void ProcessMessage(SoapMessage message) { throw new NotImplementedException(); } } } 

这就是它在配置中的连线方式:

                        

这种方法的问题似乎只适用于Web应用程序,尝试在控制台应用程序中实现它不会产生任何结果。

跟踪方法

跟踪方法基于“ 配置消息日志记录”指南,在此方法中,您可以为应用程序中的每个SOAP / WCF通信启用.NET跟踪并将日志转储到某处(有关配置的更多信息,请参阅“ 建议的跟踪和消息设置”)记录 )。

这是跟踪配置的示例:

                                   

ServiceModel.svclog和MessageLogging.svclog的内容可以在GitHub的Gist中找到,因为它太大而不适合这里。

这个方法的问题是它在应用程序中记录每个 SOAP / WCF消息,似乎生成的日志并不真正有用,它们包含大量的信息,我无法理解是否以及如何只过滤我感兴趣的内容,阅读它们的唯一实用方法似乎是微软的服务跟踪查看器 。

我也试过添加自定义TraceListener:

 namespace Playground.Client { using System; using System.Diagnostics; using System.IO; using System.Text; using System.Xml; using System.Xml.Linq; public class CustomTraceListener : TraceListener { public override void Write(string message) { File.AppendAllLines("CustomTraceListener.txt", new[] { message }); } public override void WriteLine(string message) { message = this.FormatXml(message); File.AppendAllLines("CustomTraceListener.txt", new[] { message }); } private string FormatXml(string message) { using (var stringWriter = new StringWriter()) { var xmlWriterSettings = new XmlWriterSettings(); xmlWriterSettings.Encoding = Encoding.UTF8; xmlWriterSettings.Indent = true; xmlWriterSettings.OmitXmlDeclaration = true; using (var xmlTextWriter = XmlWriter.Create(stringWriter, xmlWriterSettings)) { XDocument.Parse(message).Save(xmlTextWriter); } return Convert.ToString(stringWriter); } } } } 

但即使它允许我拦截消息,它也不会保存任何元数据:

   POST   uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA     http://tempuri.org/IGreeterService/SayHello 80101cc1-dfb5-4c8e-8d19-ec848ab69100    Albireo       http://tempuri.org/IGreeterService/SayHello http://localhost:8080/greeter   POST   uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA     80101cc1-dfb5-4c8e-8d19-ec848ab69100    Albireo     

有了这些信息,就不可能重建请求/响应流,因为所有消息都混合在一起。

将它们与本机XmlWriterTraceListener生成的* .svclog进行比较:

   0 3 0 8      ESP-DEV-9       http://tempuri.org/IGreeterService/SayHello http://localhost:8080/greeter   POST   uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA     80101cc1-dfb5-4c8e-8d19-ec848ab69100    Albireo           0 3 0 8      ESP-DEV-9       OK OK  207 text/xml; charset=utf-8 Tue, 16 Jul 2013 08:50:04 GMT Microsoft-HTTPAPI/2.0       Hello, Albireo.         

这里标签建立了每个请求和响应之间的关系,允许开发人员重建整个会话。

有没有办法完成我想要做的事情?

如果服务注册为WCF Web服务(而不是旧式ASMX Web服务),则可以通过IClientMessageInspectorIEndpointBehavior

首先,您必须创建一个inheritance自IClientMessageInspector的类, IClientMessageInspector将处理请求和回复的日志记录。

 namespace Playground.Sandbox { using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; public class MyClientMessageInspector : IClientMessageInspector { public object BeforeSendRequest( ref Message request, IClientChannel channel) { // TODO: log the request. // If you return something here, it will be available in the // correlationState parameter when AfterReceiveReply is called. return null; } public void AfterReceiveReply( ref Message reply, object correlationState) { // TODO: log the reply. // If you returned something in BeforeSendRequest // it will be available in the correlationState parameter. } } } 

然后,您必须创建一个inheritance自IEndpointBehavior的类, IEndpointBehavior将在客户端中注册检查器。

 namespace Playground.Sandbox { using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; public class MyEndpointBehavior : IEndpointBehavior { public void Validate( ServiceEndpoint endpoint) { } public void AddBindingParameters( ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior( ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void ApplyClientBehavior( ServiceEndpoint endpoint, ClientRuntime clientRuntime) { var myClientMessageInspector = new MyClientMessageInspector(); clientRuntime.ClientMessageInspectors.Add(myClientMessageInspector); } } } 

然后,当您想要使用该行为时,您可以在使用该服务之前手动注册它。

 namespace Playground.Sandbox { public static class Program { public static void Main() { using (var client = new MyWcfClient()) { var myEndpointBehavior = new MyEndpointBehavior(); client.Endpoint.Behaviors.Add(myEndpointBehavior); // TODO: your things with the client. } } } } 

如果您不想手动注册该行为,或者您需要将其始终保持活动状态,则可以在配置文件中注册该行为。

首先,您需要创建一个inheritance自BehaviorExtensionElement的类,该类将告诉.NET Framework将应用哪些行为,并在需要时创建实例。

 namespace Playground.Sandbox { using System; using System.ServiceModel.Configuration; public class MyBehaviorExtensionElement : BehaviorExtensionElement { protected override object CreateBehavior() { var myEndpointBehavior = new MyEndpointBehavior(); return myEndpointBehavior; } public override Type BehaviorType { get { return typeof(MyEndpointBehavior); } } } } 

然后,您需要在配置文件中注册BehaviorExtensionElement (仅显示配置文件的相关部分)。

                        

现在您可以使用该服务,而无需每次手动注册行为:

 namespace Playground.Sandbox { public static class Program { public static void Main() { using (var client = new MyWcfService()) { // TODO: your things with the client. } } } } 

您可以在MSDN的Message Inspectors文章中找到有关如何执行此操作的指南。