次の方法で共有


カスタム メッセージ エンコーダー: カスタム テキスト エンコーダー

テキスト サンプルでは、Windows Communication Foundation (WCF) を使用してカスタム テキスト メッセージ エンコーダーを実装する方法を示します。

WCF の TextMessageEncodingBindingElement では、UTF-8、UTF-16、ビッグ エンディアン Unicode エンコードのみがサポートされます。 このサンプルのカスタム テキスト メッセージ エンコーダーは、相互運用性のために必要になる可能性がある、プラットフォームでサポートされているすべての文字エンコードをサポートしています。 このサンプルは、インターネット インフォメーション サービス (IIS) によってホストされるクライアント コンソール プログラム (.exe)、サービス ライブラリ (.dll)、テキスト メッセージ エンコーダー ライブラリ (.dll) で構成されています。 このサービスは、要求/応答通信パターンを定義するコントラクトを実装します。 コントラクトは、算術演算 (加算、減算、乗算、除算) を公開する ICalculator インターフェイスによって定義されます。 クライアントは特定の算術演算に対して同期要求を行い、サービスは結果と共に応答します。 クライアントとサービスの両方で、既定のCustomTextMessageEncoderではなくTextMessageEncodingBindingElementが使用されます。

カスタム エンコーダーの実装は、メッセージ エンコーダー ファクトリ、メッセージ エンコーダー、メッセージ エンコード バインド要素、および構成ハンドラーで構成され、次の例を示します。

  • カスタム エンコーダーとエンコーダー ファクトリの構築。

  • カスタム エンコーダーのバインド要素を作成する。

  • カスタム バインド要素を統合するためのカスタム バインド構成の使用。

  • カスタム バインド要素のファイル構成を許可するカスタム構成ハンドラーの開発。

サンプルを設定、ビルド、実行するには

  1. 次のコマンド ASP.NET 使用して 4.0 をインストールします。

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Windows Communication Foundation サンプル One-Time セットアップ手順を実行していることを確認します。

  3. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。

  4. 単一または複数のコンピューター間の構成でサンプルを実行するには、「Windows Communication Foundation Samplesの実行」の手順に従います。

メッセージ エンコーダー ファクトリとメッセージ エンコーダー

ServiceHostまたはクライアント チャネルが開かれると、デザイン時コンポーネントCustomTextMessageBindingElementによってCustomTextMessageEncoderFactoryが作成されます。 ファクトリによって CustomTextMessageEncoderが作成されます。 メッセージ エンコーダーは、ストリーミング モードとバッファー モードの両方で動作します。 XmlReaderXmlWriterを使用して、メッセージの読み取りと書き込みを行います。 UTF-8、UTF-16、ビッグ エンディアン Unicode のみをサポートする WCF の最適化された XML リーダーとライターとは対照的に、これらのリーダーとライターは、プラットフォームでサポートされているすべてのエンコードをサポートします。

次のコード例は、CustomTextMessageEncoder を示しています。

public class CustomTextMessageEncoder : MessageEncoder
{
    private CustomTextMessageEncoderFactory factory;
    private XmlWriterSettings writerSettings;
    private string contentType;

    public CustomTextMessageEncoder(CustomTextMessageEncoderFactory factory)
    {
        this.factory = factory;

        this.writerSettings = new XmlWriterSettings();
        this.writerSettings.Encoding = Encoding.GetEncoding(factory.CharSet);
        this.contentType = $"{this.factory.MediaType}; charset={this.writerSettings.Encoding.HeaderName}";
    }

    public override string ContentType
    {
        get
        {
            return this.contentType;
        }
    }

    public override string MediaType
    {
        get
        {
            return factory.MediaType;
        }
    }

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.factory.MessageVersion;
        }
    }

    public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
    {
        byte[] msgContents = new byte[buffer.Count];
        Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
        bufferManager.ReturnBuffer(buffer.Array);

        MemoryStream stream = new MemoryStream(msgContents);
        return ReadMessage(stream, int.MaxValue);
    }

    public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
    {
        XmlReader reader = XmlReader.Create(stream);
        return Message.CreateMessage(reader, maxSizeOfHeaders, this.MessageVersion);
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
    {
        MemoryStream stream = new MemoryStream();
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();

        byte[] messageBytes = stream.GetBuffer();
        int messageLength = (int)stream.Position;
        stream.Close();

        int totalLength = messageLength + messageOffset;
        byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
        Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);

        ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
        return byteArray;
    }

    public override void WriteMessage(Message message, Stream stream)
    {
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();
    }
}

次のコード例は、メッセージ エンコーダー ファクトリを構築する方法を示しています。

public class CustomTextMessageEncoderFactory : MessageEncoderFactory
{
    private MessageEncoder encoder;
    private MessageVersion version;
    private string mediaType;
    private string charSet;

    internal CustomTextMessageEncoderFactory(string mediaType, string charSet,
        MessageVersion version)
    {
        this.version = version;
        this.mediaType = mediaType;
        this.charSet = charSet;
        this.encoder = new CustomTextMessageEncoder(this);
    }

    public override MessageEncoder Encoder
    {
        get
        {
            return this.encoder;
        }
    }

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.version;
        }
    }

    internal string MediaType
    {
        get
        {
            return this.mediaType;
        }
    }

    internal string CharSet
    {
        get
        {
            return this.charSet;
        }
    }
}

メッセージ エンコーディング バインディング要素

バインド要素を使用すると、WCF ランタイム スタックを構成できます。 WCF アプリケーションでカスタム メッセージ エンコーダーを使用するには、ランタイム スタック内の適切なレベルで適切な設定を使用してメッセージ エンコーダー ファクトリを作成するバインド要素が必要です。

CustomTextMessageBindingElementは、BindingElement基底クラスから派生し、MessageEncodingBindingElement クラスから継承します。 これにより、他の WCF コンポーネントは、このバインディング要素をメッセージ エンコード バインド要素として認識できます。 CreateMessageEncoderFactoryの実装では、適切な設定で一致するメッセージ エンコーダー ファクトリのインスタンスが返されます。

CustomTextMessageBindingElementは、プロパティを使用してMessageVersionContentType、およびEncodingの設定を公開します。 エンコーダーでは、Soap11Addressing バージョンと Soap12Addressing1 バージョンの両方がサポートされています。 既定値は Soap11Addressing1 です。 ContentTypeの既定値は "text/xml" です。 Encoding プロパティを使用すると、目的の文字エンコードの値を設定できます。 サンプル クライアントとサービスは、WCF の TextMessageEncodingBindingElement ではサポートされていない ISO-8859-1 (Latin1) 文字エンコードを使用します。

次のコードは、カスタム テキスト メッセージ エンコーダーを使用してバインディングをプログラムで作成する方法を示しています。

ICollection<BindingElement> bindingElements = new List<BindingElement>();
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
CustomTextMessageBindingElement textBindingElement = new CustomTextMessageBindingElement();
bindingElements.Add(textBindingElement);
bindingElements.Add(httpBindingElement);
CustomBinding binding = new CustomBinding(bindingElements);

メッセージ エンコード バインド要素へのメタデータ サポートの追加

MessageEncodingBindingElementから派生するすべての型は、サービス用に生成された WSDL ドキュメント内の SOAP バインディングのバージョンを更新します。 これを行うには、ExportEndpoint インターフェイスに IWsdlExportExtension メソッドを実装し、生成された WSDL を変更します。 このサンプルでは、 CustomTextMessageBindingElementTextMessageEncodingBindingElementからの WSDL エクスポート ロジックを使用します。

このサンプルでは、クライアント構成は手動で構成されています。 CustomTextMessageBindingElementでは動作を記述するポリシー アサーションがエクスポートされないため、Svcutil.exe を使用してクライアント構成を生成することはできません。 通常、カスタム バインド要素に IPolicyExportExtension インターフェイスを実装して、バインディング要素によって実装される動作または機能を記述するカスタム ポリシー アサーションをエクスポートする必要があります。 カスタム バインド要素のポリシー アサーションをエクスポートする方法の例については、 トランスポート: UDP サンプルを参照してください。

メッセージ エンコーディング バインド構成ハンドラー

前のセクションでは、カスタム テキスト メッセージ エンコーダーをプログラムで使用する方法を示します。 CustomTextMessageEncodingBindingSectionは、構成ファイル内でカスタム テキスト メッセージ エンコーダーの使用を指定できる構成ハンドラーを実装します。 CustomTextMessageEncodingBindingSection クラスは、BindingElementExtensionElement クラスから派生します。 BindingElementType プロパティは、このセクション用に作成するバインド要素の種類を構成システムに通知します。

CustomTextMessageBindingElementによって定義されたすべての設定は、CustomTextMessageEncodingBindingSectionのプロパティとして公開されます。 ConfigurationPropertyAttributeは、構成要素の属性をプロパティにマッピングし、属性が設定されていない場合は既定値を設定するのに役立ちます。 構成の値が読み込まれ、型のプロパティに適用されると、 CreateBindingElement メソッドが呼び出され、プロパティがバインド要素の具象インスタンスに変換されます。

この構成ハンドラーは、サービスまたはクライアントの App.config または Web.config の次の表現にマップされます。

<customTextMessageEncoding encoding="utf-8" contentType="text/xml" messageVersion="Soap11Addressing1" />

このサンプルでは、ISO-8859-1 エンコードを使用します。

この構成ハンドラーを使用するには、次の構成要素を使用して登録する必要があります。

<extensions>
    <bindingElementExtensions>
        <add name="customTextMessageEncoding" type="
Microsoft.ServiceModel.Samples.CustomTextMessageEncodingBindingSection,
                  CustomTextMessageEncoder" />
    </bindingElementExtensions>
</extensions>