共用方式為


自訂訊息編碼器:自訂文字編碼器

文字範例示範如何使用 Windows Communication Foundation (WCF) 實作自訂文字訊息編碼器。

WCF 的 TextMessageEncodingBindingElement 只支援 UTF-8、UTF-16 和 Big Endean Unicode 編碼方式。 這個範例中的自訂文字訊息編碼器會支援所有平台支援的字元編碼,這是為達成互通性而可能需要的編碼方式。 這個範例是由用戶端主控台程式 (.exe)、網際網路資訊服務 (IIS) 裝載的服務程式庫 (.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 範例的一次性安裝程序

  3. 若要建置解決方案,請依照建置 Windows Communication Foundation 範例中的指示操作。

  4. 若要在單一或多部電腦組態中執行此範例,請遵循執行 Windows Communication Foundation 範例中的指示進行。

訊息編碼器處理站和訊息編碼器

開啟 ServiceHost 或用戶端通道時,設計階段元件 CustomTextMessageBindingElement 會建立 CustomTextMessageEncoderFactory。 處理站則會建立 CustomTextMessageEncoder。 訊息編碼器會同時以資料流處理模式和緩衝模式來運作。 它會分別使用 XmlReaderXmlWriter 來讀取和寫入訊息。 正好與僅支援 UTF-8、UTF-16 和 Big-Endean 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 會透過屬性公開 MessageVersionContentTypeEncoding 的設定。 編碼器同時支援 Soap11Addressing 和 Soap12Addressing1 版本。 預設為 Soap11Addressing1。 ContentType 的預設值為 "text/xml"。 Encoding 屬性可讓您設定所需的字元編碼值。 範例用戶端與服務會使用 ISO-8859-1 (Latin1) 字元編碼,但是 WCF 的 TextMessageEncodingBindingElement 不支援這種編碼方式。

下列程式碼將示範如何使用自訂文字訊息編碼器,透過程式設計方式建立繫結。

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,即可做到這點。 在這個範例中,CustomTextMessageBindingElement 將使用 TextMessageEncodingBindingElement 的 WSDL 匯出邏輯。

在這個範例中,用戶端組態會以手動方式設定。 由於 CustomTextMessageBindingElement 並不匯出原則判斷提示來描述其行為,您將無法使用 Svcutil.exe 產生用戶端組態。 您通常應該在自訂繫結項目上實作 IPolicyExportExtension 介面,以便匯出可描述繫結項目所實作之行為或功能的自訂原則判斷提示。 如需如何為自訂繫結項目匯出原則判斷提示的範例,請參閱 Transport: UDP 範例。

訊息編碼繫結組態處理常式

上一個區段示範的是如何以程式設計方式使用自訂文字訊息編碼器。 CustomTextMessageEncodingBindingSection 將會實作可讓您在組態檔中指定自訂文字訊息編碼器使用方式的組態處理常式。 CustomTextMessageEncodingBindingSection 類別衍生自 BindingElementExtensionElement 類別。 BindingElementType 屬性會通知組態系統要為此區段建立的繫結項目類型。

CustomTextMessageBindingElement 定義的所有設定都會公開為 CustomTextMessageEncodingBindingSection 中的屬性。 ConfigurationPropertyAttribute 有助於將組態項目屬性 (Attribute) 對應至屬性 (Property),並可在屬性 (Attribute) 未設定時設定其預設值。 載入來自組態的值並將這些值套用至型別的屬性時,就會呼叫 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>