Пользовательский кодировщик сообщений: кодировщик пользовательских текстовых сообщений

В примере текста показано, как реализовать пользовательский кодировщик текстовых сообщений с помощью Windows Communication Foundation (WCF).

WCF TextMessageEncodingBindingElement поддерживает только кодировки Юникода UTF-8, UTF-16 и big-endian Юникод. Кодировщик пользовательского текстового сообщения в этом примере поддерживает все кодировки символов, поддерживаемые платформой, которые могут потребоваться для взаимодействия. Пример состоит из клиентской консольной программы (.exe), библиотеки служб (.dll), размещенной службы IIS (IIS), и библиотеки кодировщика текстовых сообщений (.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. Кодировщик сообщений работает как в режиме буферизации, так и в режиме потоковой передачи. Он использует классы XmlReader и XmlWriter для чтения и записи сообщений соответственно. В отличие от оптимизированных средств чтения XML и записей WCF, поддерживающих только UTF-8, UTF-16 и big-endian Юникод, эти средства чтения и записи поддерживают всю кодировку, поддерживаемую платформой.

В следующем примере кода показан кодировщик 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 предоставляет параметры для MessageVersion, ContentType и Encoding через свойства. Кодировщик поддерживает версии Soap11Addressing и Soap12Addressing1. Значение по умолчанию - Soap11Addressing1. Значение по умолчанию ContentType - "text/xml". Свойство Encoding позволяет задать значение требуемой кодировки символов. В примере клиента и службы используется кодировка символов ISO-8859-1 (Latin1), которая не поддерживается TextMessageEncodingBindingElement 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, отвечает за обновление версии привязки протокола SOAP в документе WSDL, созданном для службы. Этого можно добиться, реализовав метод ExportEndpoint в интерфейсе IWsdlExportExtension, а затем изменив созданный язык WSDL. В этом образце CustomTextMessageBindingElement использует логику экспорта WSDL из TextMessageEncodingBindingElement.

В этом образце конфигурация клиента настраивается вручную. Для создания конфигурации клиента нельзя использовать средство 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>