C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2007/12/09 16:35
SOAP メッセージの検証・変更を行うための PowerShell 関数を作成しました。

【 Set-MessageInspector 関数 】
WCF クライアントが要求メッセージを送信する前、応答メッセージを受信した後の 2 箇所に独自の処理を追加します。
追加した処理からは、要求メッセージまたは応答メッセージの検査・変更を行うことができます。
これにより、例えば、SOAP メッセージをログに記録することができます。

[ パラメータ ]
・target
SvcUtil.exe が生成したクライアントクラス ( ClientBase ジェネリッククラスの派生クラス ) のインスタンスを指定します。
または、チャネルファクトリのインスタンスを指定します。

・beforeSendRequest
WCF クライアントが要求メッセージを送信する前に行う処理を指定します。
beforeSendRequest に指定する関数内では、要求メッセージ (System.ServiceModel.Channels.Message) を第一引数から取得できます。
また、WCF クライアント オブジェクト チャネル (System.ServiceModel.IClientChannel) を第二引数から取得できます。
関数が戻り値を返した場合、戻り値は afterReceiveReply に指定した関数の第二引数に、相関状態データとして渡されます。

・afterReceiveReply
WCF クライアントが応答メッセージを受信した後に行う処理を指定します。
afterReceiveReply に指定する関数内では、応答メッセージ (System.ServiceModel.Channels.Message) を第一引数から取得できます。
また、相関状態データ (System.Object) を第二引数から取得できます。


[ 戻り値 ]
なし


[ コード ]
function Set-MessageInspector
{
    param ($target, [ScriptBlock]$beforeSendRequest, [ScriptBlock]$afterReceiveReply)
    trap { break; }
   
    function Compile-MessageInspectorCode
    {
        $source =
@"
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;

internal sealed class PSClientMessageInspector : IClientMessageInspector
{
    private static object InvokeScript(ScriptBlock target, params object[] args)
    {
        StringBuilder argsTextBuilder = new StringBuilder();
        if (args != null)
        {
            for (int i = 0; i <= args.Length; i++)
            {
                argsTextBuilder.AppendFormat("`$invokeArgs[{0}] ", i);
            }
        }
        string invokeCommandText = string.Format("`$invokeArgs = `$Input | % {{ `$_; }}; &{{{0}}} {1}", target, argsTextBuilder);
        Command invokeCommand = new Command(invokeCommandText, true, true);
        using (Pipeline invokePipeline = Runspace.DefaultRunspace.CreateNestedPipeline())
        {
            invokePipeline.Commands.Add(invokeCommand);
            Collection<PSObject> invokeResult = invokePipeline.Invoke(args);
            object result = null;
            if ((invokeResult != null) && (invokeResult.Count != 0))
            {
                result = (invokeResult.Count == 1) ? (object)invokeResult[0] : (object)invokeResult;
            }
            return result;
        }
    }
   
    private readonly ScriptBlock _beforeSendRequestScriptBlock;
   
    private readonly ScriptBlock _afterReceiveReplyScriptBlock;
   
    public PSClientMessageInspector(ScriptBlock beforeSendRequestScriptBlock, ScriptBlock afterReceiveReplyScriptBlock)
    {
        this._beforeSendRequestScriptBlock = beforeSendRequestScriptBlock;
        this._afterReceiveReplyScriptBlock = afterReceiveReplyScriptBlock;
    }
   
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        if (this._beforeSendRequestScriptBlock == null)
        {
            return null;
        }
        return PSClientMessageInspector.InvokeScript(this._beforeSendRequestScriptBlock, request, channel);
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        if (this._afterReceiveReplyScriptBlock == null)
        {
            return;
        }
        PSClientMessageInspector.InvokeScript(this._afterReceiveReplyScriptBlock, reply, correlationState);
    }
}

public sealed class PSClientMessageInspectorBehavior : IEndpointBehavior
{
    private readonly PSClientMessageInspector _inspector;
   
    public PSClientMessageInspectorBehavior(ScriptBlock beforeSendRequestScriptBlock, ScriptBlock afterReceiveReplyScriptBlock)
    {
        this._inspector = new PSClientMessageInspector(beforeSendRequestScriptBlock, afterReceiveReplyScriptBlock);
    }
   
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(this._inspector);
    }
   
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }
   
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }
   
    public void Validate(ServiceEndpoint endpoint)
    {
    }
}
"@;
        $references =
            @(
                [System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel").Location,
                [System.Reflection.Assembly]::LoadWithPartialName("System.Management.Automation").Location
            );
        Compile-CSCode $source $references | Out-Null;
    }
   
    $behaviors = $target.Endpoint.Behaviors;
    if ($() -eq $behaviors)
    {
        throw "引数 client が、 System.ServiceModel.ClientBase``1 の派生型、または System.ServiceModel.ChannelFactory``1 の派生型ではありません。";
    }
   
    if ($() -eq ("PSClientMessageInspectorBehavior" -as [Type]))
    {
        Compile-MessageInspectorCode;
    }
    $inspectorBehavior = New-Object "PSClientMessageInspectorBehavior" @($beforeSendRequest, $afterReceiveReply);
    $behaviors.Add($inspectorBehavior);
}



[ 注意 ]
・この関数は、内部で Compile-CSCode 関数 を使用しています。
Compile-CSCode 関数が使用できない場合、この関数は実行に失敗します。

・この関数で取得できる SOAP メッセージは、実際にネットワーク上で通信される SOAP メッセージとは異なる場合がありますので注意してください。
例えば、バインディングにWS-Security が適用されているとネットワーク上で通信される SOAP メッセージは暗号化されますが、この関数で取得した SOAP メッセージは暗号化前もしくは復号後のものになります。
ネットワーク上で実際に通信される SOAP メッセージを調べるには、Microsoft Network Monitor 等のツールを使用してください。


[ 使用例 ]

$client = New-WCFClient Sample.WCF.ServiceClient "C:\Work\WCF\Client.dll.config";
$beforeSendRequest =
    {
        param ($request, $channel)
        return [DateTime]::Now;
    };
$afterReceiveReply =
    {
        param ($reply, $correlationState)
        "requested time: {0}" -f $correlationState | Write-Host;
        $reply | Write-Host;
    };
Set-MessageInspector $client $beforeSendRequest $afterReceiveReply;


このコマンドは、New-WCFClient 関数 を使用して生成されたWCF クライアントのインスタンスに、応答の SOAP メッセージをコンソール出力するための独自の処理を追加します。
応答の SOAP メッセージの出力時には、要求メッセージを送信した日時も出力します。


【 ダウンロード 】
自作の PowerShell 関数は、以下の記事からまとめてダウンロードできます。

YokoKen.PowerShell.Scripts


これは便利ですね。
使わせていただきますe-2

2007.12.20 23:23 URL | Anthony #- [ 編集 ]


どうぞ使ってやってください^^

ただ、一つ重要なことを書き忘れてました。記事内の [ 注意 ] に追記しておきましたので、ご一読願います。 ( これじゃちょっと使えないか・・・ orz )

2007.12.21 13:29 URL | よこけん #Ay6tTHf6 [ 編集 ]












トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/194-5c458091