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

文本示例演示如何使用 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 示例 执行One-Time 安装过程。

  3. 要生成解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。

  4. 若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。

消息编码器工厂和消息编码器

ServiceHost 或客户端通道打开时,设计时组件 CustomTextMessageBindingElement 可创建 CustomTextMessageEncoderFactory。 该工厂创建 CustomTextMessageEncoder。 消息编码器在流式处理模式和缓冲模式下运行。 它分别使用 XmlReaderXmlWriter 来读取和写入消息。 与仅支持 UTF-8、UTF-16 和 big-endian 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 的实现返回与相应设置匹配的消息编码器工厂的实例。

通过属性公开CustomTextMessageBindingElementMessageVersionContentTypeEncoding的设置。 编码器支持 Soap11Addressing 和 Soap12Addressing1 版本。 默认值为 Soap11Addressing1。 默认值 ContentType 为“text/xml”。 使用该 Encoding 属性可以设置所需字符编码的值。 示例客户端和服务使用 ISO-8859-1(Latin1)字符编码,而 WCF 不支持此编码。

以下代码演示如何使用自定义文本消息编码器以编程方式创建绑定。

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 接口,以导出描述绑定元素实现的行为或功能的自定义策略断言。 有关如何导出自定义绑定元素的策略断言的示例,请参阅 Transport: 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>