4

WCF メッセージから SOAP ヘッダー全体を削除しようとしていますが、エンベロープ本体のみを残したいだけです。どうすればそれを行うことができるか、誰でも私にアイデアを与えることができますか?

次のような WCF メッセージを作成します。

**string response = "Hello World!";
Message msg = Message.CreateMessage(MessageVersion.Soap11, "*", new TextBodyWriter(response));
msg.Headers.Clear();**

送信する SOAP メッセージは次のようになります。

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <Binary>Hello World!</Binary>
  </s:Body>
</s:Envelope>

しかし、SOAP ヘッダー要素は必要ありません。エンベロープ本体だけが必要です。WCF メッセージからヘッダー要素を削除するにはどうすればよいですか?

4

4 に答える 4

7

オプション 1: bacicHttpBinding を使用します。コンテンツはヘッダーに追加されません (セキュリティ用に構成されていない場合)。

オプション 2: カスタム メッセージ エンコーダーを実装し、ヘッダーを削除します。その前のどこでも、wcf がヘッダーを追加し直す可能性があります。ここでサンプルエンコーダーを参照してください。

于 2012-06-27T09:59:29.393 に答える
4

その質問はトリッキーなものです: 一歩一歩考えてみましょう

いくつかのコンテキスト

クラスは、メソッドMessageにヘッダーを書き込みます。次に、書き込みを開始する内部オーバーロードを呼び出します。ToString()ToString()ToString(XmlDictionaryWriter writer)

// System.ServiceModel.Channels.Message
internal void ToString(XmlDictionaryWriter writer)
{
    if (this.IsDisposed)
    {
        throw TraceUtility.ThrowHelperError(this.CreateMessageDisposedException(), this);
    }
    if (this.Version.Envelope != EnvelopeVersion.None)
    {
        this.WriteStartEnvelope(writer);
        this.WriteStartHeaders(writer);
        MessageHeaders headers = this.Headers;
        for (int i = 0; i < headers.Count; i++)
        {
            headers.WriteHeader(i, writer);
        }
        writer.WriteEndElement();
        MessageDictionary arg_60_0 = XD.MessageDictionary;
        this.WriteStartBody(writer);
    }
    this.BodyToString(writer);
    if (this.Version.Envelope != EnvelopeVersion.None)
    {
        writer.WriteEndElement();
        writer.WriteEndElement();
    }
}

このthis.WriteStartHeaders(writer);コードは、ヘッダーの数に関係なく、ヘッダー タグを書き込みます。writer.WriteEndElement()for ループの後に一致します。これは、書き込まれているヘッダー タグと一致するwriter.WriteEndElement() 必要があります。そうでない場合、Xml ドキュメントは無効になります。

したがって、ヘッダーを取り除くために仮想メソッドをオーバーライドする方法はありません。仮想メソッドをWriteStartHeaders呼び出しますOnWriteStartHeadersが、タグを閉じると、単純にそれを止めることができなくなります)。ToString()ヘッダー関連の構造を削除するには、メソッド全体を変更する必要があります。

- write start of envelope
- write start of body
- write body
- write end of body
- write end of envelope

ソリューション

上記の疑似コードでは、「書き込み本文」部分以外のすべてを制御できます。最初に呼び出されるすべてのメソッドは、ToString(XmlDictionaryWriter writer)を除いて publicBodyToStringです。したがって、リフレクションまたはニーズに合った方法で呼び出す必要があります。ヘッダーなしでメッセージを書くと、単純に次のようになります。

private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
{
    msg.WriteStartEnvelope(writer); // start of envelope
    msg.WriteStartBody(writer); // start of body

    var bodyToStringMethod = msg.GetType()
        .GetMethod("BodyToString", BindingFlags.Instance | BindingFlags.NonPublic);
    bodyToStringMethod.Invoke(msg, new object[] {writer}); // write body

    writer.WriteEndElement(); // write end of body
    writer.WriteEndElement(); // write end of envelope
}

これで、ヘッダーなしでメッセージ コンテンツを取得する方法ができました。しかし、このメソッドはどのように呼び出せばよいのでしょうか?

ヘッダーのないメッセージを文字列としてのみ必要です

ToString()メッセージの最初の書き込みを呼び出すメソッドをオーバーライドする必要はありません。プログラムで と を受け取るメソッドを作成し、MessageそれXmlDictionaryWriterを呼び出してヘッダーなしでメッセージを取得するだけです。

ToString()メソッドがヘッダーなしでメッセージを返すようにしたい

これはもう少し複雑です。System.ServiceModel アセンブリから多くMessageの依存関係を引き出す必要があるため、クラスから簡単に継承することはできません。この回答ではそこには行きません。

私たちができることは、いくつかのフレームワークの機能を使用して、既存のオブジェクトの周りにプロキシを作成し、元のオブジェクトへの呼び出しをインターセプトして、その動作を置き換え/強化することです。Castle Dynamic プロキシに慣れているので、それを使用しましょう。

ToString() メソッドをインターセプトしたいので、Message使用しているオブジェクトの周りにプロキシを作成し、インターセプターを追加して、のToStringメソッドをMessage実装に置き換えます。

var msg = Message.CreateMessage(MessageVersion.Soap11, "*");
msg.Headers.Clear();

var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
var proxiedMessage = proxyGenerator.CreateClassProxyWithTarget(msg, new ProxyGenerationOptions(),
    new ToStringInterceptor());

最初のメソッドToStringInterceptorとほぼ同じことを行う必要がありますが、ToString()上記で定義した ProcessMessage メソッドを使用します。

public class ToStringInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method.Name != "ToString")
        {
            invocation.Proceed();
        }
        else
        {
            var result = string.Empty;
            var msg = invocation.InvocationTarget as Message;

            StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
            XmlDictionaryWriter xmlDictionaryWriter =
                XmlDictionaryWriter.CreateDictionaryWriter(new XmlTextWriter(stringWriter));

            try
            {
                ProcessMessage(msg, xmlDictionaryWriter);
                xmlDictionaryWriter.Flush();
                result = stringWriter.ToString();
            }
            catch (XmlException ex)
            {
                result = "ErrorMessage";
            }
            invocation.ReturnValue = result;
        }
    }

    private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
    {
        // same method as above
    }
}

メッセージの ToString() メソッドを呼び出すと、ヘッダーのないエンベロープが返されるようになりました。メッセージをフレームワークの他の部分に渡すことができ、それがほとんど機能するはずであることがわかります。メッセージの内部配管の一部への直接呼び出しは、初期出力を生成できますが、完全な再実装がなければ、それを制御できません。

注意点

  • これは、私が見つけたヘッダーを削除する最短の方法です。ライターでのヘッダーのシリアル化が 1 つの仮想関数だけで処理されていないことが大きな問題でした。このコードでは、あまり動き回る余地がありません。
  • この実装は、XmlWriterの元の実装で使用されたものと同じものを使用しません。このクラスは System.ServiceModel の内部にあり、それを引き出すことは読者への演習として残されています。その結果、xml は単純なI 使用でフォーマットされていないため、出力はわずかに異なります。ToString()MessageEncodingFallbackAwareXmlTextWriterXmlTextWriter
  • インターセプターは、最初のToString()呼び出しから返された xml を単純に解析し、ヘッダー ノードを削除してから値をバブルアップさせることができました。これは別の実行可能なソリューションです。

生コード

public class ToStringInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method.Name != "ToString")
        {
            invocation.Proceed();
        }
        else
        {
            var result = string.Empty;
            var msg = invocation.InvocationTarget as Message;

            StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
            XmlDictionaryWriter xmlDictionaryWriter =
                XmlDictionaryWriter.CreateDictionaryWriter(new XmlTextWriter(stringWriter));

            try
            {
                ProcessMessage(msg, xmlDictionaryWriter);
                xmlDictionaryWriter.Flush();
                result = stringWriter.ToString();
            }
            catch (XmlException ex)
            {
                result = "ErrorMessage";
            }
            invocation.ReturnValue = result;
        }
    }

    private void ProcessMessage(Message msg, XmlDictionaryWriter writer)
    {
        msg.WriteStartEnvelope(writer);
        msg.WriteStartBody(writer);

        var bodyToStringMethod = msg.GetType()
            .GetMethod("BodyToString", BindingFlags.Instance | BindingFlags.NonPublic);
        bodyToStringMethod.Invoke(msg, new object[] { writer });

        writer.WriteEndElement();
        writer.WriteEndElement();
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var msg = Message.CreateMessage(MessageVersion.Soap11, "*");
        msg.Headers.Clear();

        var proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
        var proxiedMessage = proxyGenerator.CreateClassProxyWithTarget(msg, new ProxyGenerationOptions(),
            new ToStringInterceptor());

        var initialResult = msg.ToString();
        var proxiedResult = proxiedMessage.ToString();

        Console.WriteLine("Initial result");
        Console.WriteLine(initialResult);
        Console.WriteLine();
        Console.WriteLine("Proxied result");
        Console.WriteLine(proxiedResult);

        Console.ReadLine();
    }
}
于 2014-10-22T10:27:01.723 に答える