Compartilhar via


Codificador de mensagem personalizado: Codificador de texto personalizado

O Exemplo de texto demonstra como implementar um codificador de mensagens de texto personalizado usando o WCF (Windows Communication Foundation).

O TextMessageEncodingBindingElement do WCF dá suporte apenas às codificações UNIcode UTF-8, UTF-16 e big-endian. O codificador de mensagens de texto personalizado neste exemplo dá suporte a todas as codificações de caracteres compatíveis com a plataforma que podem ser necessárias para interoperabilidade. Esta amostra consiste em um programa de console do cliente (.exe), uma biblioteca de serviços (.dll) hospedada pelos Serviços de Informações da Internet (IIS) e uma biblioteca do codificador de mensagens de texto (.dll). O serviço implementa um contrato que define um padrão de comunicação solicitação-resposta. O contrato é definido pela interface ICalculator, que expõe operações matemáticas (adicionar, subtrair, multiplicar e dividir). O cliente faz solicitações síncronas para uma determinada operação matemática, e o serviço responde com o resultado. O cliente e o serviço usam o CustomTextMessageEncoder em vez do padrão TextMessageEncodingBindingElement.

A implementação do codificador personalizado consiste em uma fábrica de codificador de mensagens, um codificador de mensagens, um elemento de associação de codificação de mensagens e um manipulador de configuração e demonstra o seguinte:

  • Criando um codificador personalizado e uma fábrica de codificadores.

  • Criando um elemento de associação para um codificador personalizado.

  • Usando a configuração de associação personalizada para integrar elementos de associação personalizados.

  • Desenvolvendo um manipulador de configuração personalizado para permitir a configuração de arquivo de um elemento de associação personalizado.

Para configurar, compilar, e executar o exemplo

  1. Instale o ASP.NET 4.0 usando o seguinte comando.

    %windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
    
  2. Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.

  3. Para compilar a solução, siga as instruções contidas em Como compilar as amostras do Windows Communication Foundation.

  4. Para executar a amostra em uma configuração de computador único ou cruzado, siga as instruções em Como executar os exemplos do Windows Communication Foundation.

Fábrica do Codificador de Mensagens e o Codificador de Mensagens

Quando o ServiceHost ou o canal ou cliente é aberto, o componente de tempo de design CustomTextMessageBindingElement cria o CustomTextMessageEncoderFactory. O alocador cria o CustomTextMessageEncoder. O codificador de mensagens opera tanto no modo de streaming quanto no modo em buffer. Ele usa o XmlReader e XmlWriter para ler e gravar as mensagens, respectivamente. Ao contrário dos leitores e escritores XML otimizados do WCF que dão suporte apenas a UTF-8, UTF-16 e Unicode de big-endian, esses leitores e escritores dão suporte a todas as codificações com suporte de plataforma.

O exemplo de código a seguir mostra o 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();
    }
}

O exemplo de código a seguir mostra como criar o alocador do codificador de mensagens.

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;
        }
    }
}

Elemento associação de codificação de mensagens

Os elementos de associação permitem a configuração da pilha de tempo de execução do WCF. Para usar o codificador de mensagens personalizado em um aplicativo WCF, é necessário um elemento de associação que cria a fábrica do codificador de mensagens com as configurações apropriadas no nível apropriado na pilha de tempo de execução.

O CustomTextMessageBindingElement deriva da classe base BindingElement e herda da classe MessageEncodingBindingElement. Isso permite que outros componentes do WCF reconheçam esse elemento de associação como sendo um elemento de associação de codificação de mensagens. A implementação de CreateMessageEncoderFactory retorna uma instância da fábrica do codificador de mensagens correspondente com as configurações apropriadas.

O CustomTextMessageBindingElement expõe as configurações para MessageVersion, ContentType e Encoding através de propriedades. O codificador dá suporte às versões Soap11Addressing e Soap12Addressing1. O padrão é Soap11Addressing1. O valor padrão do ContentType é "text/xml". A propriedade Encoding permite que você defina o valor da codificação de caracteres desejada. O cliente e o serviço de exemplo usam a codificação de caracteres ISO-8859-1 (Latin1), que não tem suporte do TextMessageEncodingBindingElement do WCF.

O código a seguir mostra como criar programaticamente a associação usando o codificador de mensagem de texto personalizado.

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);

Adicionando suporte a metadados ao elemento de associação de codificação de mensagens

Qualquer tipo derivado de MessageEncodingBindingElement é responsável por atualizar a versão da associação SOAP no documento WSDL gerado para o serviço. Isso é feito implementando o método ExportEndpoint na interface IWsdlExportExtension e modificando o WSDL gerado. Neste exemplo, o CustomTextMessageBindingElement usa a lógica de exportação do WSDL do TextMessageEncodingBindingElement.

Neste exemplo, a configuração do cliente está configurada manualmente. Você não pode usar Svcutil.exe para gerar a configuração do cliente porque o CustomTextMessageBindingElement não exporta uma declaração de política para descrever seu comportamento. Geralmente, você deve implementar a interface IPolicyExportExtension em um elemento de associação personalizado para exportar uma declaração de política personalizada que descreve o comportamento ou a capacidade implementada pelo elemento de associação. Para obter um exemplo de como exportar uma declaração de política para um elemento de associação personalizado, confira a amostra Transporte: UDP.

Manipulador de configuração de associação de codificação de mensagens

A seção anterior mostra como usar o codificador de mensagem de texto personalizado programaticamente. O CustomTextMessageEncodingBindingSection implementa um manipulador de configuração que permite especificar o uso de um codificador de mensagem de texto personalizado em um arquivo de configuração. A classe CustomTextMessageEncodingBindingSection é derivada da classe BindingElementExtensionElement. A propriedade BindingElementType informa o sistema de configuração do tipo de elemento de associação a ser criado para esta seção.

Todas as configurações definidas por CustomTextMessageBindingElement são expostas como as propriedades no CustomTextMessageEncodingBindingSection. O ConfigurationPropertyAttribute auxilia no mapeamento dos atributos do elemento de configuração para as propriedades e na configuração de valores padrão se o atributo não estiver definido. Depois que os valores da configuração são carregados e aplicados às propriedades do tipo, o método CreateBindingElement é chamado, que converte as propriedades em uma instância concreta de um elemento de associação.

Esse manipulador de configuração mapeia para a seguinte representação no App.config ou Web.config para o serviço ou cliente.

<customTextMessageEncoding encoding="utf-8" contentType="text/xml" messageVersion="Soap11Addressing1" />

O exemplo usa a codificação ISO-8859-1.

Para usar esse manipulador de configuração, ele deve ser registrado usando o elemento de configuração a seguir.

<extensions>
    <bindingElementExtensions>
        <add name="customTextMessageEncoding" type="
Microsoft.ServiceModel.Samples.CustomTextMessageEncodingBindingSection,
                  CustomTextMessageEncoder" />
    </bindingElementExtensions>
</extensions>