WCF – Hosting


Como já sabemos, WCF – Windows Communication Foundation – é parte integrante do .NET Framework 3.0. Ele fornece um conjunto de classes para a construção e hospedagem de serviços baseando-se na arquitetura SOA (Service Oriented Architecture), podendo expor tais serviços para serem acessados através dos mais diversos tipos de protocolos, como por exemplo: HTTP, TCP, IPC e MSMQ. Atualmente existem três tipos de hosts para serviços construídos em WCF: self-hosting (através da classe ServiceHost), IIS (Internet Information Services) e WAS (Windows Activation Services – Windows Vista) e, é exatamente isso que este artigo abordará, ou seja, como configurar cada um desses hosts para expor os serviços construídos em WCF.

Como o foco do artigo é exclusivamente a configuração do host, vou considerar como conhecimento prévio o procedimento para a criação de serviço básico de WCF, principalmente no que diz respeito aos atributos que devem ser aplicados ao serviço para torná-lo um serviço WCF. Para o exemplo, vamos criar um contrato padrão, chamado de IServiceContract, que será implementado pelos serviços concretos. Esse contrato (uma Interface) terá dois métodos simples, chamados de BoasVindas e HoraAtual. Essa Interface está exibida abaixo:

using System;
using System.ServiceModel;

namespace DevMinds.ComponentCS
{
    [ServiceContract]
    public interface IServiceContract
    {
        [OperationContract]
        string BoasVindas();

        [OperationContract]
        DateTime DataAtual();
    }
}

Self-Hosting

O self-hosting trata de uma aplicação, seja ela Console, Windows Forms, Windows Service, etc., que expõe um ou vários serviços para serem consumidos por seus clientes. Para que seja possível expor o serviço programaticamente, ou seja, sem utilizar o IIS ou o WAS, primeiramente é necessário fazer referência ao arquivo System.ServiceModel.dll. Esse Assembly contém todas as classes necessárias para trabalhar com Windows Communication Foundation – WCF – e fornece também a classe ServiceHost. Essa classe fornece toda a infraestrutura necessária para expor serviços WCF.

Um dos overloads do construtor da classe ServiceHost recebe um objeto do tipo Type, onde devemos apontar qual será o tipo que o host irá expor. Em seguida, é necessário definir o endpoint que indicará em que local e com qual protocolo o serviço será exposto e, para atender essa necessidade, a classe ServiceHost fornece um método chamado AddServiceEndpoint para criar quantos endpoints forem necessários para o serviço. A classe ServiceHost ainda fornece alguns eventos que podem ser úteis para fins de monitoramento. Esses eventos estão descritos na tabela abaixo:

Evento Descrição
Closed

Ocorre quando a comunicação com o objeto é fechada.

Closing

Ocorre quando a comunicação com o objeto estiver sendo fechada.

Faulted

Ocorre quando uma falha acontece.

Opened

Ocorre quando a comunicação com o objeto é aberta.

Opening

Ocorre quando a comunicação com o objeto estiver sendo aberta.

UnknownMessageReceived

Ocorre quando uma mensagem desconhecida é recebida.

Para que os clientes possam extrair as informações (metadados) necessárias a respeito do serviço, precisamos expor a descrição do mesmo (que descreve todas as funcionalidades fornecidas pelo serviço, endpoints e seus comportamentos) para que os desenvolvedores possam referenciá-lo nas aplicações clientes que irão consumí-lo. Você pode expor essas informações através do protocolo HTTP (via método Get) ou utilizar um endpoint dedicado. O Windows Communication Foundation disponibiliza os metadados automaticamente para o serviço através de HTTP-GET e, mais adiante, veremos como disponibilizar o contrato através de outros endpoints, com protocolos diferentes. Tudo o que você precisa fazer é habilitar isso, adicionando explicitamente um service behavior.

Resumindo, o MEX (metadata exchange endpoint) é extremamente necessário, já que o cliente necessita importar para ter localmente a representação do serviço e, conseqüentemente, invocá-lo. Para configurarmos um serviço a ser exposto, podemos fazer via código (Visual C# ou Visual Basic .NET) ou declarativamente, através do arquivo *.Config da aplicação. Como discutimos acima, um serviço WCF fornece, por padrão, a descrição do mesmo através do protocolo HTTP-GET; mas para isso, precisamos apenas habilitá-lo, através do atributo httpGetEnabled do elemento serviceMetadata, se estivermos falando da configuração via arquivo *.Config e se quisermos expor a descrição via HTTP-GET.

Ainda há a possibilidade de expor a descrição do serviço através de um endpoint customizado, podendo acessá-lo através de um outro protocolo, como por exemplo o TCP. Este cenário exige a criação de um endpoint simples, como já fazemos normalmente para um serviço qualquer, mas neste caso, o tipo de contrato a ser exposto será baseado na Interface IMetadataExchange, que está definida dentro do namespace System.ServiceModel.Description, o qual expõe métodos que retornam metadados referentes ao serviço.

Os endpoints (que expõem o serviço e os metadados) podem ser configurados declarativamente ou programaticamente. Iremos analisar essas duas formas. Para fins de exemplo, utilizaremos um projeto do tipo Console Application mas, um pouco adiante, falaremos sobre as diversas aplicações que podem servir como host de serviço WCF. O código abaixo exemplifica como criar via código o endpoint necessário para expor o serviço e um outro endpoint para o MEX, ambos utilizando protocolo TCP:

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

using DevMinds.ComponentCS;

static void Main(string[] args)
{
    using (ServiceHost host = new ServiceHost(typeof(DefaultService),
        new Uri("net.tcp://localhost:9000/")))
    {
        host.Description.Behaviors.Add(new ServiceMetadataBehavior());

        host.AddServiceEndpoint(
            typeof(IMetadataExchange),
            MetadataExchangeBindings.CreateMexTcpBinding(),
            "MEX/");

        host.AddServiceEndpoint(
            typeof(IServiceContract),
            new NetTcpBinding(),
            "DefaultService/");

        host.Open();
        Console.ReadLine();
    }
}

Como podemos notar, criamos a instância da classe ServiceHost, informando em seu construtor o tipo que a mesma irá servir e em qual endereço irá disponibilizá-la. Em seguida, adicionamos uma instância da classe ServiceMetadataBehavior dentro de uma coleção chamada Behaviors relacionada ao host recém criado; este passo é necessário porque o host valida o tipo de contrato especificado nos endpoints, ou seja, o contrato do endpoint deve ser compatível com o contrato declarado na criação do ServiceHost. No caso acima, a classe que criamos, DefaultService, implementa a Interface IServiceContract, mas não implementa a Interface IMetadataExchange. Internamente quando o host detecta que os tipos não são compatíveis, ele analisa os behaviors que foram registrados; se ele encontrar uma instância da classe ServiceMetadataBehavior, somente com ela presente será possível adicionar o endpoint de metadados, que já está logo em seguida. Neste momento, entra em cena uma classe estática chamada MetadataExchangeBindings. Essa classe fornece membros estáticos que auxiliam na criação de bindings exclusivos para cada um dos protocolos, que irá depender da forma de comunicação que deseja ter com os clientes para saber qual desses métodos utilizar.

A mesma configuração acima dentro do arquivo *.Config fica da seguinte forma (note que no atributo contract do elemento endpoint é necessário especificar a interface do contrato do serviço, incluindo os namespaces até ela):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="DevMinds.ComponentVB.DefaultService" behaviorConfiguration="MEX">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9000/"/>
          </baseAddresses>
        </host>
        <endpoint
          contract="IMetadataExchange"          
          binding="mexTcpBinding"
          address="MEX/" />
        <endpoint
          contract="DevMinds.ComponentVB.IServiceContract"
          binding="netTcpBinding"
          address="DefaultService/" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MEX">
          <serviceMetadata />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Nota: É importante dizer que a configuração via arquivo *.Config não dispensa a criação do objeto ServiceHost no código e sua abertura através do método Open. A única diferença neste caso, é que não há a necessidade de informar o baseAddress, behaviors e os endpoints.

Com o host devidamente configurado, independente se foi através de código declarativo (*.Config) ou via código, basta inicializar a aplicação e, através do endereço que foi especificado para criar o serviço, você pode adicionar a referencia na aplicação cliente e, em seguida, já conseguirá consumir o serviço.

Como falamos acima, o self-hosting permite utilizarmos uma aplicação .NET para disponibilizá-lo. Existem três tipos de aplicações que podem atender essa necessidade: Console Application (onde baseamos o nosso teste), Windows Forms e Windows Service. A Console Application é muito útil quando estamos efetuando testes ou mesmo em tempo de desenvolvimento e jamais é indicada para o ambiente de produção, já que necessita de um usuário logado na máquina onde o executável reside e, ainda podemos acidentalmente fechar o host, o que evitaria que requisições subseqüentes sejam processadas.

A outra opção é a utilização de criar o host em uma aplicação Windows Forms. Ela é um pouco melhor em relação a Console Application, mas ainda exige que um usuário acesse o sistema para que possa inicializar o host. Finalmente, temos o Windows Service. Este sim é um dos mais apropriados tipos de aplicações para servir como host de uma serviço WCF, pois não necessita que o usuário esteja logado para que funcione e ainda pode ser configurado para ser inicializado automaticamente quando o computador for ligado. Independente disso, o host já deve estar no ar quando a requisição chegar, o que já não acontece com o IIS, pois ele ativa o objeto assim que a requisição chega ao servidor. Analisaremos o host através do IIS na próxima seção.

IIS – Internet Information Services

Até o lançamento do WCF tínhamos (e ainda temos) à disposição dentro da plataforma .NET, a possibilidade de construirmos os famosos Web Services (ASMX). Os Web Services foram lançados desde a versão 1.0 do .NET Framework e revolucionou a forma de como expor dados e componentes para consumo. Eles, por sua vez, estão fortemente ligados à arquitetura do ASP.NET, onde a dependência do protocolo HTTP e algumas de suas funcionalidades específicas são uma das “piores” partes, já que quebra alguns dos conceitos que a arquitetura SOA impõe.

Os serviços WCF também podem utilizar o IIS como hosting, de forma bem semelhante aos Web Services (ASMX), tirando alguns proveitos do mesmo; um dos principais é a ativação do processo, que é feita quando uma requisição chega até o servidor. Mas, como nem tudo é perfeito, o IIS limita o tipo de transporte, obrigando-nos a utilizar somente o protocolo HTTP.

Para que seja possível fazer o host de um serviço dentro do IIS, é necessário que você tenha um arquivo físico com a extensão *.svc. Esse tipo de arquivo está associado com um serviço do WCF e, quando a requisição para um recurso deste tipo chega no servidor, ele é tratado através de um módulo chamado HttpModule em conjunto com um handler chamado HttpHandler, que estão contidos dentro do namespace System.ServiceModel.Activation (isso pode ser confirmado através do arquivo Web.Config que está dentro do seguinte endereço: %windir%Microsoft.NETFrameworkv2.0.50727CONFIG). A forma com que estes objetos trabalham irá depender do modo de compatibilidade com o ASP.NET que pode estar ou não estar habilitado. Analisaremos os dois modos existentes mais adiante, ainda neste artigo.

Os arquivo com extensão *.svc tem uma diretiva ServiceHost, onde informamos a linguagem que estaremos escrevendo o código, o nome do serviço e o caminho até o arquivo de CodeBehind do serviço, que reside dentro da pasta especial App_Code. Isso é bem semelhante à estrutura de uma página ASP.NET. O código abaixo mostra a estrutura, de apenas uma linha, de um arquivo *.svc:

[ C# ]
<%@ ServiceHost Language="C#" 
                   Debug="true" 
                   Service="DefaultService"
                   CodeBehind="~/App_Code/DefaultService.cs" %>

Dentro do arquivo de CodeBehind criamos uma classe e implementamos a Interface IServiceContract (já comentada mais acima). Depois de realizada a devida implementação devida realizada, definindo efetivamente o que os métodos irão fazer, é necessário criarmos o endpoint no arquivo Web.Config para que o IIS consiga identificar e expor o serviço. A configuração é bem semelhante ao arquivo App.Config que vimos acima, que configura o serviço em um self-hosting. Ligeiras mudanças são realizadas, conforme é mostrado abaixo:

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="DefaultService" behaviorConfiguration="returnFaults">
        <endpoint 
          contract="DevMinds.ComponentCS.IServiceContract" 
          binding="wsHttpBinding"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="returnFaults">
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

É importante analisar que na criação do endpoint não é necessário o atributo address, pois o endereço (base address) será determinado a partir do endereço do arquivo *.svc. Outro detalhe importante é com relação ao atributo httpGetEnabled do elemento serviceMetadata que, quando estiver definido como True, é responsável por habilitar que o contrato do serviço seja publicado e, conseqüentemente, acessado pelos clientes. Finalmente, o atributo includeExceptionDetailInFaults, que é utilizado para propósitos de depuração pois, quando é definido como True, propaga qualquer exceção atirada pelo serviço para os clientes, facilitando a depuração quando estamos consumindo o serviço em um ambiente de testes. Claro que essa informação somente deve ser disponibilizada quando o serviço ainda está em processo de desenvolvimento, pois se essas informações chegarem aos clientes finais, você pode estar abrindo brechas para que ele possa explorar as informações de erro, inclusive o stack trace.

Ao invocar o serviço no browser, já conseguimos ter acesso ao mesmo, inclusive ao seu contrato. A imagem abaixo ilustra o serviço sendo apresentado no browser:

Figura 1 – Output do serviço sendo invocado no browser.

Neste momento o serviço já está pronto para ser consumido pelos clientes e, como podemos notar na imagem acima, ela mesma exibe um exemplo de como gerar o proxy no cliente, através do utilitário svcutil.exe ou ainda, se estiver utilizando o Visual Studio .NET 2005 com o .NET Framework 3.0 devidamente instalado, pode ir diretamente no projeto onde deseja consumir o serviço, clicar com o botão direito do mouse em cima do mesmo e, em seguida, clicar na opção “Add Service Reference…”, informando o endereço HTTP acima, que automaticamente o proxy será gerado e adicionado à aplicação.

Podemos tranquilamente criar um diretório virtual dentro do IIS para hospedar os serviços WCF. Porém há cenários onde os serviços WCF estão dentro do IIS, mas contidos em uma aplicação ASP.NET ou até mesmo em uma aplicação do tipo Web Services (ASMX). Em todas as ocasiões, muitas vezes é necessário fazer o uso de algumas funcionalidades específicas do protocolo HTTP para atender uma determinada necessidade, como por exemplo, a utilização de variáveis de aplicação (Application), sessão (Session) ou até mesmo informações contidas no contexto HTTP (HttpContext). O WCF, em conjunto com o runtime do ASP.NET, fornece dois modos de execução de um pedido para um serviço, que tratam de forma bem diferente a requisição ao *.svc. Essas formas são: Mixed Transports Mode ou ASP.NET Compatibility Mode, as quais falaremos a seguir.

Na primeira delas, Mixed Transports Mode (modo default), a requisição do serviço não é executada pelo ASP.NET pipeline. Isso permite definir endpoints com diferentes formas de transportes. Uma das características mais importantes deste modo é que ele não permite que serviços WCF façam uso de funcionalidades específicas do ASP.NET, mais precisamente do protocolo HTTP. Entre as funcionalidades desabilitadas em virtude de ter esse modo habilitado temos:

Funcionalidade Desabilitada Descrição
HttpContext.Current

Em uma aplicação tradicional Web, a classe HttpContext encapsula informações específicas a respeito da requisição HTTP, que estão acessíveis através da propriedade estática Current.

Em um serviço WCF, essa propriedade sempre retornará nula, mas existe uma classe chamada OperationContext que, através de uma propriedade estática, também chamada Current, retorna informações a respeito conteúdo atual da requisição.

File e UrlAuthorization

A autorização, que é efetuada a partir da seção authorization no arquivo Web.Config, está também desabilitada. Para autorização, você precisará implementar isso nas mensagens que são enviadas/recebidas pelos serviços WCF.

Impersonation

A capacidade de impersonificação que o ASP.NET fornece através do elemento identity passa a ficar desabilitada neste modo.

Session State

As variáveis de sessões não são suportadas neste modo (lembrem-se que isso difere bastante as Sessões do WCF, que serão abordados em um futuro artigo).

Outras

Outras funcionalidades também estão desabilitadas, como é o exemplo da globalização (acessada através do elemento globalization). Além disso, outras funcionalidades que dependem da classe HttpContext, também podem não funcionar corretamente, como é o caso da seção AppSettings.

Já o ASP.NET Compatibility Mode trata de um modo que habilita todas as restrições impostas pelo modo anterior, mas impõe um grande ponto negativo, ou seja, o serviço fica completamente dependente do protocolo HTTP e, sendo assim, quando o serviço estiver sendo ativado, os endpoints serão verificados para assegurar que protocolos não-HTTP estão habilitados.

Existe um elemento no arquivo Web.Config que permite-nos declarativamente habilitar ou desabilitar esse modo. Trata-se do atributo aspNetCompatibilityEnabled do elemento serviceHostingEnvironment. Quando definido como False (padrão), o modo utilizado é o Mixed Transports Mode e, quando True, o modo ASP.NET Compatibility Mode está habilitado. Mas, para habilitar o modo ASP.NET Compatibility Mode, ainda é preciso de uma configuração adicional, mas desta vez diretamente na classe que representa e implementa o contrato do serviço, que é decorar a classe com o atributo AspNetCompatibilityRequirements, que está contido dentro do namespace System.ServiceModel.Activation e configurá-lo de acordo com a necessidade. Esse atributo recebe, através da propriedade RequirementsMode, um dos três valores contidos no enumerador AspNetCompatibilityRequirementsMode, os quais são explicados abaixo:

Opção Descrição
Allowed

Esta opção indica que o serviço pode rodar no modo ASP.NET Compatibility. Mas, internamente, deverá se comportar bem quando esse recurso não estiver habilitado.

NotAllowed

Não permite que o serviço rode em modo ASP.NET Compatibility. Essa opção é utilizada em cenário onde o serviço não é construído exclusivamente para HTTP.

Required

Indica que o serviço exige o modo ASP.NET Compatibility para rodar e, neste caso, poderá fazer o uso das características acima sem nenhuma preocupação, já que essa opção garantirá que o serviço irá rodar em um ambiente onde as características específicas de HTTP estarão habilitadas.

Os trechos de código abaixo, com algumas partes suprimidas por questões de espaço, ilustram a configuração na classe do serviço e no arquivo Web.Config para habilitar o ASP.NET Compatibility Mode:

using System.ServiceModel.Activation;

[AspNetCompatibilityRequirements(
    RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
public class DefaultService : IServiceContract
{
    //Restante da implementação
}

 

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <services>
      <service name="DefaultService">
        <!-- Endpoints -->
      </service>
    </services>
    <!-- Outras Configurações -->
  </system.serviceModel>
</configuration>

Um pouco acima falamos sobre o módulo HttpModule e o handler HttpHandler, que fazem parte da execução do processamento de uma requisição para um serviço WCF. A forma de trabalho irá depender exclusivamente do modo que está habilitado. Se o modo Mixed Transports estiver habilitado, o módulo HttpHandler irá interceptar a requisição logo após o evento PostAuthenticateRequest e, neste momento, o objeto HttpContext.Current é definido como nulo. Agora, quando o modo ASP.NET Compatibility estiver habilitado, o módulo HttpModule será um simples filtro, sem fazer nada de mais importante. Depois da execução do módulo, o handler é finalmente executado, para efetivamente fazer o trabalho do serviço WCF.

WAS – Windows Activation Services

O Windows Vista fornece um novo recurso chamado WAS – Windows Activation Services. Trata-se de um processo que é instalado dentro do IIS 7, que desacopla a arquitetura de ativação do IIS, permitindo assim o transporte não somente via HTTP, mas também via named pipes, TCP e MSMQ (message queue).

Um outro ponto importante é que, além de suportar múltiplos protocolos, fornece as funcionalidades dadas pelo IIS, como por exemplo o health monitoring, reciclagem de processo e, conseqüentemente, ferramentas de gerenciamento para tais configurações.

Quando o listener de um determinado protocolo recebe a requisição, o WAS verifica a existência do work process para serví-lo e, antes de processar a requisição, o handler assegura que uma instância da classe ServiceHost tenha sido criada. Assim como vimos no IIS, felizmente o WAS utiliza também o modelo de ativação baseado na mensagem, o que aumenta a escalabilidade para as requisições em qualquer protocolo.

O WAS, por padrão, não vêm instalado por padrão. É necessário fazer alguns ajustes para habilitá-lo e, conseqüentemente, disponibilizar serviços WCF para serem consumidos nos mais diversos protocolos. Deixarei a configuração e utilização do WAS dentro do IIS 7 (com Windows Vista) para um futuro artigo, dedicando-o para explorar completamente esse novo recurso.

Conclusão: Como podemos notar no decorrer deste artigo, todo o host basicamente deverá ser um processo executável, que terá um AppDomain onde o serviço será carregado. Agora, com todas essas alternativas, você deve analisar e ser capaz de escolher qual das soluções é mais viável à sua necessidade, sempre pesando os prós e contras de cada um dos tipos de host.

Anúncios

6 comentários sobre “WCF – Hosting

  1. Complementando…

    me refiro as configurações do serviço no Windows Service que está instalado no servidor remoto.

    Obrigado,

    Parabéns pelo artigo.

    • Olá.

      No seu exemplo, o endereço localhost do net.tcp é o seguinte…

      <baseAddresses>
      <add baseAddress="net.tcp://localhost:9000/"/>
      </baseAddresses>

      Para eu acessar remotamente este endereço exemplo como funciona? preciso colocar o IP do servidor no baseAddress ou localhost?

      ficaria assim?

      <baseAddresses>
      <add baseAddress="net.tcp://IP:9000/"/>
      </baseAddresses>

      Para rodar no sevidor remoto preciso fazer alguma configuração adicional além de liberar a porta?

      Obrigado.

    • Boas Gil,

      Sim, você tem que referenciar via IP ou através do nome da máquina.

      Como você já desconfia, é necessário também que eventuais firewalls, liberem este porta.

    • Na realidade foi pesquisando aplicações com uso de TCP e HTTP que cheguei até aqui. Como você entende muito dessa área me permita fazer uma pergunta:

      Tenho um sistema de automação que usa o sistema GPRS onde os rádios com chips da OI comunicam-se através de um software servidor que esta instalado em uma máquina com IP fixo. Esta máquina pertence a uma rede corporativa e utiliza o mesmo link da OI/TELEMAR para se comunicar com as estações remotas via celular. A OI criou para mim uma APN(VPN) onde esse máquina citada com Windows XP esta inserida. Ao ser inserida na APN em conexões de rede aparece um icone de um computador escrito conexões de entrada. O aplicativo SCC server é iniciado mas não consigo me conectar com nenhum dos radios com chip da OI. Voce poderia me dar uma ajuda com relação à configuração dessa conexão no WIN XP? É necessário configurar o WIN XP de forma específica para atuar como servidor? Por coincidencia a porta de comunicação é a 9000 citada no seu aplicativo – parte dele.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s