自定义消息编码器:自定义文本编码器

文本示例演示如何使用 Windows Communication Foundation (WCF) 实现自定义文本消息编码器。

WCF 的 TextMessageEncodingBindingElement 仅支持 UTF-8、UTF-16 和 Big-Endean Unicode 编码。 本示例中的自定义文本消息编码器支持所有受平台支持的字符编码,这些字符编码可能是实现互操作性所需的。 本示例包括客户端控制台程序 (.exe)、Internet 信息服务 (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 应用程序中使用自定义消息编码器,则需要一个绑定元素,该元素在运行时堆栈中的相应级别使用相应的设置来创建消息编码器工厂。

CustomTextMessageBindingElementBindingElement 基类派生并从 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 导出逻辑。

对于本示例,客户端配置是手动配置的。 无法使用 Svcutil.exe 生成客户端配置,因为 CustomTextMessageBindingElement 未导出策略断言来描述其行为。 通常应该实现自定义绑定元素上的 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>