Поделиться через


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

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

ms751486.Warning(ru-ru,VS.100).gif Внимание!
Образцы уже могут быть установлены на компьютере. Перед продолжением проверьте следующий каталог (по умолчанию).

<диск_установки>:\WF_WCF_Samples

Если этот каталог не существует, перейдите на страницу Образцы Windows Communication Foundation (WCF) и Windows Workflow Foundation (WF) для .NET Framework 4, чтобы загрузить все образцы Windows Communication Foundation (WCF) и WF. Этот образец расположен в следующем каталоге.

<диск_установки>:\WF_WCF_Samples\WCF\Extensibility\MessageEncoder\Text

TextMessageEncodingBindingElement платформы WCF поддерживает только кодировки UTF-8, UTF-16 и Big Endean Unicode. Пользовательский кодировщик текстовых сообщений в этом образце поддерживает все поддерживаемые платформой кодировки символов, которые могут требоваться для взаимодействия. Этот образец содержит консольную программу клиента (EXE), библиотеку службы (DLL), размещаемую в службах 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. Чтобы выполнить образец на одном или нескольких компьютерах, следуйте инструкциям раздела Running the Windows Communication Foundation Samples.

Фабрика кодировщика сообщений и кодировщик сообщений

При открытии ServiceHost или канала клиента компонент времени разработки CustomTextMessageBindingElement создает фабрику CustomTextMessageEncoderFactory. Фабрика создает кодировщик CustomTextMessageEncoder. Кодировщик сообщений работает как в режиме буферизации, так и в режиме потоковой передачи. Он использует классы XmlReader и XmlWriter для чтения и записи сообщений соответственно. В отличие от оптимизированных средств чтения и записи XML платформы WCF, которые поддерживают только кодировки UTF-8, UTF-16 и Big-Endean Unicode, эти средства чтения и записи поддерживают все кодировки, поддерживаемые платформой.

В следующем примере кода показан кодировщик 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 = string.Format("{0}; charset={1}", 
                this.factory.MediaType, 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 из TextMessageEncodingBinidngElement.

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