Capturando mensagens genéricas

A primeira etapa para a construção de um serviço WCF é a definição do contrato do serviço que, durante a escrita do código, é determinado pela criação de uma interface tradicional. Os métodos que são criados ali são, em tempo de execução, expostos através do WSDL para que os clientes possam consumí-los.

Se criarmos uma referencia para o serviço a partir da IDE do Visual Studio ou do utilitário svcutil.exe, automaticamente um proxy será criado e podemos invocar os métodos a partir dele, com a impressão de que se estivéssemos chamando um método localmente mas que, durante a execução, uma mensagem é criada e enviada ao respectivo método remoto.

Mas e quando não temos o essa flexibilidade de criar o proxy (exemplo)? Como fazemos para invocar o um método disponibilizado pelo serviço? Felizmente podemos recorrer ao Fiddler ou até mesmo as classes HttpWebRequest e HttpWebResponse para efetuar a requisição, montando o cabeçalho e o corpo (SOAP) para invocar um determinado método do serviço. Esse procedimento permite a quem estiver consumindo, colocar o nome errado do método a ser invocado e, consequentemente, o seu serviço irá rejeitar a requisição.

Felizmente o WCF nos permite lidar com mensagens genéricas, ou seja, podemos criar um método que capturará toda e qualquer mensagem que não for encontrada no contrato do serviço. Para isso, basta definirmos a propriedade Action e ReplyAction do atributo OperationContractAttribute para “*”. Isso fará com que todas as mensagens não encontradas na definição do contrato, sejam encaminhadas para este método, em que podemos efetuar logging, notificações, etc. Abaixo um exemplo ilustra como proceder:

[OperationContract(Action=”*”, ReplyAction=”*”)]
Message ProcessRequest(Message msg);

É importante notar que o método recebe e também retorna uma instancia da classe Message. Esta classe representa a mensagem dentro do WCF. Além disso, também podemos usar esta mesma técnica quando utilizamos o modelo REST, só que agora, devemos definir a propriedade UriTemplate do o atributo WebGetAttribute para “*” e, assim como o OperationContractAttribute, consiga capturar todas mensagens não mapeadas/existentes.

[OperationContract]
[WebGet(UriTemplate=”*”)]
Message ProcessRequest(Message msg);

Transição do Cliente para o Serviço (Debugger)

O Visual Studio .NET 2008 traz uma funcionalidade que permite aos desenvolvedores de serviços WCF, efetuar a depuração do mesmo através do cliente, ou seja, quando estamos executando o cliente em modo de depuração, ao chamar uma operação através da instancia do proxy (via F11 – Step Into), automaticamente seremos redirecionados para o método do serviço, executando-o passo-à-passo.

Ao pressionar F11, o debugger automaticamente irá se vincular ao processo que corresponde efetua o host do serviço e, ao finalizar a execução do método, o controle voltará ao cliente. O mais importante é que a janela Call Stack traz informações tanto do cliente como do serviço.

Caso voce não esteja fazendo uso destas funcionalidades, então provavelmente isso está desabilitado. Para habilitar este recurso, basta recorrer ao utilitário vsdiag_regwcf.exe. Este utilitário está disponível a partir do Prompt de comando do Visual Studio .NET 2008 (se estiver rodando no Windows Vista, então é necessário rodar como Administrador) e, via os seguintes parametros, voce poderá interagir com ele:

  • -i: Habilita o recurso.
  • -u: Desabilita o recurso.
  • -s: Exibe se o recurso está habilitado ou desabilitado.

UserName e Certificados

Os ASP.NET Web Services fornecem uma possibilidade de efetuar a autenticação do serviço através de SOAP Headers. Para isso, bastava criar uma classe que herde da classe SoapHeader e criar as propriedades UserName/Password e, via SoapHeaderAttribute, voce vinculava este header aos métodos que exigem a autenticação do usuário para poder funcionar.

Essa configuração funciona bem, mas é vulnerável. A questão é que o envelope SOAP irá trafegar entre o cliente e o serviço de forma desprotegida e, sendo assim, qualquer um que interceptar a requisição, conseguirá extrair essas informações confidenciais. A autenticação baseada em SOAP Headers apenas são seguras se o transporte garantir a segurança; em outras palavras, isso quer dizer HTTPS. Outra alternativa ainda utilizando os tradicionais ASP.NET Web Services, seria a utilização do WSE (Web Services Enhancements), que possibilita a segurança em nível de mensagens.

O WCF, que é seguro por padrão, tem um comportamento ligeiramente diferente em relação aos ASP.NET Web Services. Para utilizar a autenticação baseada em UserName/Password que o WCF fornece sob o protocolo HTTP, será necessário utilizar um certificado. Sem a utilização deste, não seria possível garantir a integridade e confidencialidade da mensagem, comprometendo as informações e, principalmente, permitindo que alguém intercepte a mensagem e capture os dados sigilosos. Assim como os ASP.NET Web Services, se o serviço WCF for exposto via HTTPS, isso não é necessário, mas utilizar segurança baseada em transporte tem seu ponto negativo: a segurança é apenas garantida ponto-a-ponto e, caso haja intermediários entre o cliente e o serviço, não temos a garantia de que a mensagem chegará segura até o destino.

WCF – Reliable Messages

Ao consumir um serviço WCF podemos interagir com o mesmo através de diferentes mecanismos, tais como, request-reply ou one-way (os tipos de mensagens suportados pelo WCF já foram discutidos detalhadamente neste artigo). Sabemos que, independente do tipo que você utilize, a mensagem trafega entre o cliente e o serviço através da rede, utilizando o protocolo especificado pelo binding. Com isso, uma das principais preocupações que se tem é com relação a garantia de entrega da mensagem ao seu destinatário, pois problemas com a rede podem acontecer, fazendo com que a mensagem seja interceptada ou simplesmente perdida. A finalidade deste artigo é apresentar uma técnica disponibilizada pelo WCF, para evitar que problemas como estes comprometam a consistência e execução de um serviço.

Quando desejamos enviar uma mensagem para um determinado serviço, queremos que ela chegue até o mesmo e, caso isso não aconteça, talvez precisamos reenviá-la. Mas como saber se ela chegou ou não, para tomar a decisão se devemos ou não reenviá-la? Geralmente, alguns protocolos fornecem a garantia de entrega da mensagem, como é o caso do TCP e IPC. Mesmo quando o protocolo garante nativamente a entrega da mensagem, ele somente irá assegurar a entrega ponto-a-ponto, ou seja, se houver intermediários, corremos o risco da mensagem ser perdida.

Felizmente o WCF implementa um padrão proposto pela OASIS (Organization for the Advancement of Structured Information Standards), chamado WS-ReliableMessaging (WS-RM). Essa especificação define um protoloco interoperável para transmissão de mensagens entre um único remetente para um único destinatário, garantindo que a mensagem será entregue (sem duplicação), independentemente de quantos roteadores intermediam a ligação entre eles (end-to-end). Além da garantia de entrega, este protocolo ainda fornece uma outra importante funcionalidade: você pode, opcionalmente, garantir que a entrega das mensagens aconteça na mesma ordem em que elas partiram do cliente (falaremos mais sobre ela ainda neste artigo).

Funcionamento

O funcionamento das reliable messages é um pouco complexo, mas o protocolo WS-RM em conjunto com o WCF o abstrai, facilitando o processo para aqueles que desenvolvem e administram o serviço. De qualquer forma, veremos a seguir como o processo ocorre nos bastidores do WCF (tanto no cliente quanto no serviço) para suportar essa funcionalidade.

Quando invocamos um método a partir do proxy, uma mensagem é enviada até o serviço com as informações (parâmetros, credenciais, etc.) para que o mesmo seja processado. Se temos a reliable message habilitada, não enviaremos apenas uma mensagem para o serviço (a operação que queremos invocar), mas várias outras que consistem, basicamente, na verificação para saber se a mensagem chegou ou não até o seu destino. Depois que o cliente envia a mensagem da operação, ele fica questionando o serviço para saber se a mensagem chegou até lá. O cliente espera essa notificação por um tempo (configurável) e, se o tempo exceder e a notificação não chegar, o WCF entende que ela não foi entregue, podendo agora, reenviá-la. Caso a notificação venha dentro do tempo esperado, o cliente sabe que a mensagem foi transferida com sucesso.

É importante dizer que o protocolo WS-RM foi desenhado para controlar a garantia de entrega de uma ou uma seqüência de mensagens SOAP entre dois endpoints, independentemente de como eles estão conectados, ou melhor, de quantos intermediários existam entre eles e de quais protocolos estão envolvidos. Utilizaremos a imagem abaixo para ilustrar como o processo acontece, exibindo os responsáveis que fazem isso acontecer.

Figura 1 – Funcionamento das reliable messages.

Para que isso funcione, será necessário estabelecer uma conexão com o serviço e, neste primeiro momento, um identificador será criado (CreateSequence) para correlacionar as mensagens. Quando este identificador for criado, o serviço o retorna para o cliente (CreateSequenceResponse) que irá embutí-lo em mensagens subseqüentes. Podemos perceber que ao invocar uma operação, o remetente cria um cache temporário para efetuar o rastreamento das mensagens e, a partir deste momento, o proxy é responsável por gerenciar o envio da operação e aguardar pela notificação do recebimento. Da mesma forma, o servidor também cria um cache para receber as mensagens e entregá-las para o serviço. O cache do lado do serviço terá maior utilidade quando trabalharmos com mensagens ordenadas (mais abaixo).

Ao receber a mensagem, o serviço retorna para o cliente uma mensagem contendo o elemento SequenceAcknowledgement nos headers da resposta, indicando que o serviço a recebeu. Esse elemento traz várias informações e, entre elas, temos: Identifier, AcknowledgementRange e BufferRemaining. A primeira propriedade refere-se ao identificador; já a segunda traz dois números inteiros que representam a primeira e a última mensagem processada (permitindo ao cliente remover mensagens que já estão do outro lado); finalmente, a propriedade BufferRemaining indica a quantidade disponível dentro do buffer de mensagens. A LastMessage, como o próprio nome indica, indica ao serviço que uma última mensagem será enviada e, finalmente, a mensagem TerminateSequence que encerra o processo.

Observação Importante: Quando definimos o modo de gerenciamento de instância do serviço como PerSession e habilitamos a funcionalidade de garantia de entrega, essa combinação é referida como Reliable Sessions. O protocolo WS-RM não necessita que o serviço/binding suporte sessões para funcionar, bem como o suporte à sessão não necessita do protocolo WS-RM habilitado. Ainda através da figura que vimos acima, visualizamos duas operações sendo realizadas (o que pode caracterizar uma sessão), mas é importante dizer que todos os demais passos irão ocorrer, independente da sessão estar ou não habilitada.

Configuração

A configuração das reliable messages é realizada sob o binding onde o serviço será exposto. É importante dizer que nem todas as configurações suportadas estão diretamente disponíveis através do binding, ou seja, será necessária a criação de um binding customizado para editar as configurações padrão. Os bindings expõem uma propriedade chamada ReliableSession, do tipo OptionalReliableSession que, em seu construtor, recebe uma instância da classe ReliableSessionBindingElement, contida no namespace System.ServiceModel.Channels, onde podemos efetivamente customizar o comportamento deste tipo de mensagem. A tabela abaixo exibe as propriedades expostas pela classe ReliableSessionBindingElement e que estão disponíveis para uso:

Propriedade Descrição
AcknowledgementInterval Recebe um Timespan que representa um intervalo de tempo em que o serviço aguarda para enviar a notificação de recebimento (acknowledgment). A valor padrão é de 2 segundos. Antes do serviço enviar instantaneamente a notificação de recebimento, ele aguarda este intervalo com a finalidade de agrupar o máximo de mensagens, melhorando a escalabilidade e reduzindo o tráfego de informações.
FlowControlEnabled Trata-se de um mecanismo que assegura que o remetente não envia mais mensagens quando o buffer de mensagens do serviço chega ao seu limite. Essa quantidade é informada ao remetente através da mensagem SequenceAcknowledgement, através da propriedade BufferRemaining. Essa propriedade é do tipo booleana e, quando definida como True (valor padrão), irá parar de enviar mensagens enquanto o buffer do serviço estiver cheio.
InactivityTimeout Uma propriedade que recebe um Timespan representando a duração da sessão. Se nenhuma mensagem for transmitida (incluindo mensagens de infraestrutura, como acknowledgements) durante este período, a sessão será descartada. O valor padrão é 10 minutos.
MaxPendingChannels Quando temos reliable sessions habilitadas no serviço, diferentes clientes podem estabelecer a comunicação ao mesmo tempo. Ao estabelecer a conexão, há um handshake inicial (sequences, etc.) e, após isso, o channel é colocado em uma fila com status de pendente. Esta propriedade indica quantos channels podem ser colocados neste estado e, quando omitido, o padrão é 4. Se essa fila estiver cheia qualquer tentativa de nova conexão será rejeitada.
MaxRetryCount Número inteiro que especifica a quantidade máxima de tentativas de reenvio. Enquanto o remetente não recebe a notificação de que a mensagem foi recebida pelo destinatário, o WCF reenvia a mensagem até que este limite seja atendido. Se, mesmo depois das tentativas ele não receber a notificação de recebimento, uma exceção será disparada. O valor padrão desta propriedade é 8 tentativas.
MaxTransferWindowSize Outra propriedade do tipo inteiro que define a quantidade de mensagens que o buffer pode acomodar. Do lado do cliente, esse buffer aguarda as notificações do serviço; já do lado do serviço, esse buffer acumula as mensagens para garantir que elas sejam processadas na mesma ordem que elas foram enviadas. O valor padrão para esta propriedade é 8.
Ordered Propriedade booleana que indica se as mensagens serão ou não ordenadas. O padrão é True. Maiores detalhes sobre essa técnica, serão abordados mais tarde, ainda neste artigo.

Assim como quase tudo no WCF, a configuração das reliable messages pode ser realizada tanto de forma imperativa quanto declarativa. Como já foi dito acima, a configuração é uma característica do binding, e é através dele que iremos conseguir alterar qualquer uma das propriedades que vimos na tabela acima.

Os bindings NetTcpBinding, WSHttpBinding, WSFederationHttpBinding e WSDualHttpBinding suportam as reliable messages e permitem que você habilite ou desabilite, defina um tempo de timeout por inatividade e especifique se as mensagens serão ou não ordenadas. O código abaixo ilustra as duas formas de como como podemos proceder para configurar as reliable messages no binding:

WSHttpBinding ws = new WSHttpBinding();
ws.ReliableSession.Enabled = true;
ws.ReliableSession.Ordered = true;
ws.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(5);

 

<wsHttpBinding>
    <binding name="BindingConfig">
        <reliableSession
                enabled="true"
                ordered="true"
                inactivityTimeout="00:05:00" />
    </binding>
</wsHttpBinding>

Uma outra alternativa no modo imperativo é que os bindings possuem uma versão do construtor onde já podemos definir a propriedade Enabled da ReliableSession. As outras propriedades que vimos na tabela acima não estão expostas diretamente através do binding. Apesar de muitas vezes as configurações padrões serem suficientes, em algum momento talvez seja necessário alterá-las e, para que isso seja possível, somente poderemos efetuar essa modificação através de um binding customizado, como por exemplo:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

ReliableSessionBindingElement rsbe = new ReliableSessionBindingElement();
rsbe.AcknowledgementInterval = TimeSpan.FromSeconds(5);
rsbe.FlowControlEnabled = true;
rsbe.InactivityTimeout = TimeSpan.FromMinutes(5);
rsbe.MaxPendingChannels = 10;
rsbe.MaxRetryCount = 10;
rsbe.MaxTransferWindowSize = 10;
rsbe.Ordered = true;

CustomBinding cb =
    new CustomBinding(
        new HttpTransportBindingElement(),
        new TextMessageEncodingBindingElement(),
        rsbe);

 

<customBinding>
  <binding name="BindingConfig">
    <reliableSession
      acknowledgementInterval="00:00:05"
      flowControlEnabled="true"
      inactivityTimeout="00:05:00"
      maxPendingChannels="10"
      maxRetryCount="10"
      maxTransferWindowSize="10"
      ordered="true"/>
  </binding>
</customBinding>

Habilitar a reliable message sob um determinado binding afetará o documento que descreve o serviço (WSDL), publicando nele o suporte a esse tipo de mensagem, para que os clientes consigam configurar corretamente o proxy. Algumas dessas configurações são propagadas para o cliente, como é o caso das propriedades InactivityTimeout e AcknowledgementInterval, enquanto as outras tratam-se de configurações exclusivas ao binding.

Ao invocar o método exposto pelo proxy, aparentemente apenas uma mensagem será enviada para o respectivo serviço mas, como notamos na imagem acima, várias outras mensagens são enviadas e recebidas entre o cliente e o serviço para garantir a entrega da mensagem que o cliente quer efetivamente enviar. Essas mensagens “extras” são criadas pelo próprio runtime do WCF quando habilitamos essa funcionalidade. Caso você queira visualizar para se certificar de que isso está realmente ocorrendo, basta você habilitar o tracing no cliente e conseguirá armazenar as mensagens que estão sendo trocadas. A imagem abaixo, ilustra isso (apesar de importante, o corpo da mensagem não será mostrado aqui por questões de espaço):

Figura 2 – Resultado capturado pelo tracing.

Mensagens Ordenadas

O que vimos até o momento é garantia de entrega, fornecida pelo protocolo WS-RM, e é importante ratificar que, quando habilitamos essa funcionalidade em um serviço que é exposto como PerSession, ela é referida como reliable session. Neste momento entra em cena uma configuração adicional que permite a entrega das mensagens na mesma ordem em que elas saíram. Nem sempre podemos assumir que a primeira mensagem chegará ao seu destino antes da segunda, pois elas podem optar por caminhos diferentes.

Quando esta opção está habilitada, as mensagens são enviadas para o serviço e são armazenadas no cache (ou fila) do mesmo. Se a mensagem chega na ordem correta, ela é imediatamente encaminhada para o serviço. Caso contrário, ela aguardará na fila esperando as demais mensagens para compor a seqüencia e, finalmente, ser encaminhada para o serviço. Imagine que o cliente envie as mensagens 1, 2, 3 e 4 e o serviço recebe as mensagens 1, 2 e 4, ou seja, está faltando a mensagem 3. Neste caso, a mensagem 1 e 2 serão encaminhadas para o serviço, enquanto a mensagem 4 irá aguardar a chegada da mensagem 3 para, depois disso, ser submetida para a execução.

Para habilitar este recurso (que já é o padrão) e configurá-lo, podemos utilizar o atributo DeliveryRequirementsAttribute sob a interface que representa o contrato ou sob a classe que o implementa. Onde definir dependerá do caso pois, se aplicá-lo no contrato, em qualquer classe que o implementar, ele seguirá essas configurações; se aplicar na classe que representa o serviço, então você terá uma flexibilidade para determinarem qual dos contratos você deseja aplicar essa técnica. Esse atributo fornece a propriedade RequireOrderedDelivery que é um valor booleano que indica se está ou não habilitado, e a propriedade TargetContract, que espera um objeto do tipo Type, que determina em qual contrato essa técnica será aplicada. A segunda propriedade somente faz sentido quando o atributo é aplicado na classe do serviço. O trecho de código abaixo exibe como proceder para efetuar essa configuração:

using System;
using System.ServiceModel;

[ServiceContract]
[DeliveryRequirements(RequireOrderedDelivery = true)]
public interface IContrato
{
    [OperationContract(IsOneWay = true)]
    void EnviarInformacao(string valor);
}

Conclusão: Este artigo demonstrou importantes funcionalidades que garantem a entrega e o processamento ordenado das mensagens e, como vimos, tudo isso é possível graças ao protocolo WS-ReliableMessaging. Com a implementação do protocolo WS-RM no WCF, a Microsoft conseguiu abstrair todo o trabalho complexo, permitindo aos desenvolvedores, com apenas algumas configurações simples, fazer com que ela entre em funcionamento, sem a necessidade de conhecer profundamente os detalhes que são necessários para que isso aconteça.

encodedValue

Quando fazemos uma referencia a um serviço WCF, e ele possuir um certificado definido para assegurar a troca de informações entre o cliente o e serviço, a IDE do Visual Studio ou o utilitário svcutil.exe, adicionanão um elemento chamado identity/certificate com o atributo encodedValue. O valor deste atributo é uma série de letras e números mas, o que isso representa?

Essa informação trata-se da chave pública do certificado, codificada no padrão Base64. Ela será utilizada pelo runtime do WCF para criptografar as informações que serão trocadas, mais precisamente, as credenciais. Quando essa informação não existir no cliente, a configuração do serviço deverá permitir a negociação ou informar a referencia para o certificado que, geralmente, esta armazenado em um dos repositórios do cliente.

Bloco “using” no cliente

É muito comum envolvermos objetos que utilizam recursos custosos dentro de um bloco using, fazendo com que o método Dispose do mesmo seja disparado independemente de exceções que sejam disparadas durante a execução. Vale lembrar que somente podemos envolver objetos que implementam a interface IDisposable.

Quando fazemos a referencia para algum serviço WCF em uma aplicação cliente, automaticamente a IDE do Visual Studio (ou através do utilitário svcutil.exe), se encarregará de criar uma classe que herda diretamente da classe abstrata, chamada ClientBase<TChannel>. Essa classe implementa tudo o que é necessário para possibilitar a comunicação entre cliente e o serviço (em outras palavras, trata-se do proxy) e, que por sua vez, faz uso de objetos caros (channels, etc.) e que precisam ser brevemente liberados. Como a classe ClientBase<TChannel> implementa a interface IDisposable, isso quer dizer que podemos envolver a instancia do proxy em um bloco using, para que o método Dispose seja automaticamente disparado.

Se envolvermos o proxy em um bloco using (lembre-se de que ele será transformado em try/finally), é necessário tomarmos um certo cuidado. Não porque o método Dispose não será chamado, mas sim onde que a execução deste método afetará a aplicação cliente. Vamos supor que algum erro ocorra durante a execução da operação e, como já era de se esperar, o bloco finally será disparado, chamando o método Dispose do proxy. Neste caso, o método Dispose não faz mais nada a não ser invocar o método Close. O problema aqui é que o método Close poderá exigir algumas atividades extras, necessitando fazer alguma outra comunicação com o serviço e, se neste momento algum erro ocorrer, a exceção que chegará ao cliente será:

System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

…, mascarando assim, o real problema. Finalmente, a opção para contornar esse possível problema, é a chamada do método Abort, que encerra imediatamente a comunicação entre o cliente o serviço, assim como é demonstrado neste link.

WSHttpBinding vs. WS2007HttpBinding

A Microsoft criou o WCF e implementou nele os protocolos de segurança, transações e sessões confiáveis baseando-se nas primeiras versões destes padrões, que são gerenciados pela OASIS.

Com o .NET Framework 3.5, a Microsoft atualizou o WCF, disponibilizando agora a implementação mais atualizada destes protocolos através de dois novos bindings: WS2007HttpBinding e WS2007FederationBinding.

Load Balancing em serviços WCF

Quando precisamos de escalabilidade em nossas aplicações, é muito comum recorrermos a técnicas de cluster, failover ou load balancing para que seja possível atender as muitas requisições que chegam para a mesma.

Serviços WCF não são diferentes. Serviços que estão disponíveis através do protocolo HTTP, se comportam como aplicações ASP.NET. Uma vez que eles são expostos, podem ser acessados por milhares de clientes, afetando a sua performance. Para habilitar a técnica de load balancing para serviços WCF (HTTP), é importante que voce se atente a que funcionalidades que o seu serviço WCF utiliza e, principalmente, qual a forma de gerenciamento de instância configurada.

Quando o serviço é configurado no modo PerCall, cada requisição criará uma instancia do serviço, executará a operação e, quando finalizar, a instancia criada será descartada; uma nova requisição, recria um novo objeto, executa a operação e o descarta e, assim sucessivamente. Já quando o modo é definido como PerSession, a idéia é manter uma instancia do serviço dedicada para cada proxy, atendendo a todas as operações oriundas daquele cliente.

Caso o serviço esteja em um ambiente de load balancing com o modo PerSession definido, podemos ter alguns problemas e, entre eles, a garantia de que a requisição será sempre atendida por um determinado servidor. Neste caso, perdemos o estado do serviço, trazendo resultados inesperados para este modo. O BasicHttpBinding não suporta sessão, ao contrário do WSHttpBinding que, por sua vez, emula o suporte a sessão mas que, é ineficaz neste ambiente, já que o WCF não traz suporte de gerenciamento de estado/sessão “cross-machine” como é o caso do ASP.NET.  Finalmente, o modo Single também necessita um cuidado especial, já que apenas haverá uma única instancia para atender a todas as requisições. Onde será armazenada a instancia da classe que representa o serviço?

Sempre que possível, em um ambiente de alta escalabilidade, opte pela opção PerCall. Mas quando houver a necessidade de armazenar informações entre as requisições do cliente, uma alternativa para serviços PerCall, é incluir um identificador no contrato (ou extraí-lo das informações contextuais) para salvar e/ou carregar o estado e, como repositório, deve ser utilizado um recurso compartilhado, como uma base de dados.

WCF REST Starter Kit

Há algum tempo eu escrevi sobre a possibilidade de criar serviços REST em WCF. Visando incrementar esses tipos de serviços, a Microsoft disponibilizou recentemente o Windows Communication Foundation REST Stater Kit. Este kit fornece templates de projeto e diversas funcionalidades para serviços REST criados sob WCF. As funcionalidades fornecidas pelo kit facilita o uso do protocolo HTTP dentro do serviço e encapsula algumas funcionalidades expostas pelo ASP.NET. Entre as principais funcionalidades, temos caching, tratamento/manipulador de erros, página de suporte, estensibilidade, etc.

Todas as funcionalidades estão contidas no assembly Microsoft.ServiceModel.Web.dll. O caching funciona de forma muito parecida com o caching do ASP.NET/ASP.NET Web Services, pois foi criado um atributo chamado WebCacheAttribute que, podemos aplicar em algum método de nosso serviço. Exemplo:

[WebGet(UriTemplate = “”)]
[WebCache(Duration = 30)]
[OperationContract]
string RecuperarAlgumaInformacao();

Outra possibilidade que temos, é a criação de uma página de suporte, que traz informações relacionadas aos métodos que o serviço disponibiliza. Para acessá-la, basta colocar “/help” no final da URL do serviço que, automaticamente, uma “página amigável” será exibida com essas informações. Vale lembrar que voce poderá customizar a descrição de um determinado método, através do atributo  WebHelpAttribute, como é mostrado abaixo:

[WebGet(UriTemplate = “”)]
[WebHelp(Comment = “Retorna uma informação qualquer.”)]
[OperationContract]
string RecuperarAlgumaInformacao();

Alguns novos tipos também foram estendidos para suportar essas novas funcionalidades. Entre esses tipos, temos as classes WebServiceHost2, WebServiceHost2Factory e WebHttpBehavior2. Esses tipos são semelhantes aos existentes no .NET Framework 3.5, mais precisamente, dentro do Assembly System.ServiceModel.Web.dll. O WebServiceHost2 possui uma propriedade chamada Interceptors que, como o próprio nome diz, permite-nos acoplar um “interceptador” e colocar ali algum código que desejamos executar durante a requisição.  Para criar um interceptador, tudo o que precisamos fazer é herdar da classe abstrata RequestInterceptor e sobrescrever o método ProcessRequest, como é mostrado abaixo:

public class MeuInterceptador : RequestInterceptor
{
    public MeuInterceptador() : base(true) { }

    public override void ProcessRequest(ref RequestContext requestContext)
    {
        Message msg = requestContext.RequestMessage;
        //faz algo customizado aqui
    }
}

E, depois de criado, precisamos adicioná-lo na coleção de interceptors do WebServiceHost2, o que nos obriga a criar uma service factory customizada (atente-se a sua configuração), conforme é mostrado abaixo:

internal class AppServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        WebServiceHost2 w = new WebServiceHost2(serviceType, true, baseAddresses);
        w.Interceptors.Add(new MeuInterceptador());
        return w;
    }
}

Diferentes HostHeaders no IIS

O IIS disponibiliza um recurso chamado de Web Site desde a sua versão 6.0. Os Web Sites nada mais são do que containers para diretórios virtuais que, por sua vez, poderá conter aplicações Web ou serviços. Um dos principais benefícios que ele fornece, é o isolamento de aplicações.

Além disso, o IIS utiliza um conceito chamado de IIS Binding, que identifica como acessar um determinado diretório virtual. Com isso, podemos definir definir vários host headers (IIS Bindings) para este Web Site onde, cada um deles, identificará um diretório virtual dentro do mesmo. Um exemplo disso seria: http://secure.site.com.br e http://services.site.com.br. O IIS Binding é composto por duas partes, a saber:

  • Binding Protocol: HTTP
  • Binding Information: IPAddress, Port e HostHeader

Quando temos um Web Site que hospedará serviços WCF, devemos nos atentar a essa questão. Se tivermos mais do que um IIS Binding configurado dentro do Web Site, o runtime do WCF não saberá para qual encaminhará a requisição e, conseqüentemente, uma exceção do tipo ArgumentException será lançada, contendo a seguinte mensagem:

This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection. Parameter name: item

A partir do .NET Framework 3.5, podemos fazer o uso do elemento baseAddressPrefixFilters que, quando adicionado no arquivo Web.config da aplicação onde estará o serviço, determinará qual dos host header a aplicação irá atender. O exemplo abaixo ilustra a sua utilização:

<system.serviceModel>
    <serviceHostingEnvironment>
        <baseAddressPrefixFilters>
            <add prefix=”http://teste2/”/>
        </baseAddressPrefixFilters>
    </serviceHostingEnvironment>
    <services>
        <!– configuração dos serviços –>
    </services>
</system.serviceModel>