最近、WCF サービスが処理する要求/応答をログに記録する方法を模索していました。
その前に、トレース オプションで悪い経験をしました。現在、トレースから実際の応答と要求を取得する方法はまだわかりません (svctraceviewer ツールを試しましたが、意味のあるものは得られませんでした)。それでも、トレース ファイルが破損して生成されました (予期しない文字が含まれています)。
メッセージ インスペクタ テクノロジについて知ったのは、3 ~ 4 日前のことです。私はこのマニュアルを読み、他のプロジェクトでクライアント側のメッセージ インスペクターを実装しました。実際、wcf クライアントが送信したリクエストと、そのリクエストに対する他の側のサービス レスポンスを確認できました。
別の側のロギングも同じように簡単に行えることを願っています (たとえば、クライアントからサービス要求を取得し、それらに対するサーバー応答を取得するなど)。しかし、私は寒くない。詳細は次のとおりです。
public class LogMessageBehavior : IEndpointBehavior
{
public LogMessageBehavior()
{ }
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
throw new NotImplementedException();
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
LogMessageInspector inspector = new LogMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public class LogMessageBehaviorExtensionElement : BehaviorExtensionElement
{
public LogMessageBehaviorExtensionElement() { }
public override Type BehaviorType
{
get
{
return typeof(LogMessageBehavior);
}
}
protected override object CreateBehavior()
{
return new LogMessageBehavior();
}
}
public class LogMessageInspector : IDispatchMessageInspector
{
object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
throw new NotImplementedException();
}
void IDispatchMessageInspector.BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
throw new NotImplementedException();
}
}
ほとんどすべてのコードが NotImplementedException でスタブ化されましたが、すべてのメソッドとプロパティにブレークポイントを設定しました。さらに、彼らがどのように攻撃されたかを説明します。
App.config
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="LogMessage" type="MyService.Extensions.LogMessageBehaviorExtensionElement, MyServiceApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="LogMessageEndpointBehavior">
<LogMessage />
</behavior>
</endpointBehaviors>
</behaviors>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="20000"/>
</diagnostics>
<!-- omitted for brewity -->
<services>
<service name="MyService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/"/>
</baseAddresses>
</host>
<endpoint address="MyServiceAddress" binding="basicHttpBinding" bindingConfiguration="MyService" contract="MyService" name="MyService" behaviorConfiguration="LogMessageEndpointBehavior">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="MyService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
<!-- omitted for brewity -->
</basicHttpBinding>
</bindings>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="C:\messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
サービスのインストール方法は次のとおりです。
ServiceHost myserviceHost = new ServiceHost(typeof(MyService), new Uri(Environment.bindAddress));
myserviceHost.Open();
Console.WriteLine(myserviceHost.BaseAddresses[0]);
Console.ReadLine();
サービスクラスには特別なものはありません。
では、この構造はどのように機能しますか。前に述べたように、すべてのメソッドとプロパティにブレークポイントを設定します。デバッグを開始すると、次の順序でブレークポイントがヒットしました。
1. public LogMessageBehaviorExtensionElement() { }
2. get { return typeof(LogMessageBehavior); }
3. get { return typeof(LogMessageBehavior); } (again)
4. get { return typeof(LogMessageBehavior); } (again)
5. get { return typeof(LogMessageBehavior); } (again)
OK、サービスに何かを送信して、それがどのように反応するか見てみましょう。そのため、(WSDL を使用して生成された) 要求を送信してhttp://localhost:8733/
、有効な応答を取得します。そのためにSoapUIを使用しました。また、VS にはブレークポイント アクティビティはありません。メッセージ インスペクタが機能していません。サービス メソッド ヒットのブレークポイント。メッセージ インスペクタなしでメソッドが実際に機能していることを示します。
また、このコードを使用してサービスをセットアップする動作をアタッチしようとしました(したがって、app.configで動作を指定せずに):
ServiceHost myserviceHost = new ServiceHost(typeof(MyService), new Uri(Environment.bindAddress));
myserviceHost.Open();
foreach (ServiceEndpoint endpoint in myserviceHost.Description.Endpoints)
{
endpoint.Behaviors.Add(new LogMessageBehavior());
}
Console.WriteLine(myserviceHost.BaseAddresses[0]);
Console.ReadLine();
このオプションでは、次のブレークポイント ヒットの順序が得られます。
1. public LogMessageBehavior() { }
それだ。また、サービスへのリクエストの送信に関するインスペクターのアクティビティもありません。
では、どうすれば機能させることができますか?