Consumindo um STS diretamente

No artigo anterior eu mostrei como construir um serviço WCF que faz uso do WIF para terceirizar o processo de autenticação. Lá criamos um serviço de STS utilizando os recursos fornecidos pela própria API do WIF. Como havia dito anteriormente, este serviço especial fornecido pelo WIF, nada mais é que um serviço WCF, mas que expõe um conjunto de operações específicas para lidar com a autenticação de usuários.

Quando referenciamos o serviço em uma aplicação cliente, o arquivo de configuração já é montado com todas as entradas necessárias para que o WCF em conjunto com o WIF, façam todo o processo de autenticação, e depois disso, já podemos efetuar as chamadas para as operações que o serviço (relying party) fornece. Apesar de toda a mágica ser realizada internamente, podemos explicitamente consultar o serviço de STS, para que possamos solicitar a emissão, validação ou cancelamento de um token para um determinado usuário.

Como o serviço de STS é baseado em WCF, podemos utilizar a estrutura cliente do WCF para acessá-lo. Mas para facilitar ainda mais, a Microsoft criou uma versão específica dessa estrutura, expondo versões das mesmas classes, mas voltadas para o consumo de um serviço específico de STS. Internamente essas classes substituem o comportamento padrão, acoplando os recursos fornecidos pelo WIF. A finalidade deste artigo é mostrar o conjunto de classes que temos para efetuar essa comunicação.

A primeira classe que temos é a WSTrustChannelFactory. Essa classe é responsável por criar os famosos proxies que são utilizados pelos clientes para enviar as mensagens. Mas pelo prefixo ao nome desta classe, jé podemos perceber que ela gera toda a infraestrutura para a comunicação em cima do protocolo WS-Trust, qual já discutimos em artigos anteriores. Assim como em um serviço normal, em seu construtor precisamos informar o binding e o endereço para o serviço de STS.

Aqui entra em cena alguns novos bindings, que são utilizados exclusivamente para a comunicação com serviços de STS, e que fornecem configurações pertinentes à forma de autenticação que o usuário fará. Todos os bindings herdam da classe abstrata WSTrustBindingBase, e estão debaixo do namespace Microsoft.IdentityModel.Protocols.WSTrust.Bindings. Abaixo temos a lista com cada binding suportado e sua respectiva finalidade:

  • CertificateWSTrustBinding: Permite ao cliente apresentar um certificado para a autenticação.
  • IssuedTokenWSTrustBinding: Permite ao cliente apresentar um IssuedToken para a autenticação.
  • KerberosWSTrustBinding: Permite ao cliente apresentar um token Kerberos para a autenticação.
  • UserNameWSTrustBinding: Permite ao cliente apresentar um login e senha para a autenticação.
  • WindowsWSTrustBinding: Permite ao cliente utilizar as credenciais do Windows para a autenticação.

Depois do binding, temos que definir o certificado (chave pública) que utilizaremos. Essa chave será utilizada para proteger a mensagem de envio ao serviço de STS, garantindo que somente a chave privada correspondente consigará acessar a informação, e esta, por sua vez, estará de posse do STS. Em seguida temos a definição do nome do usuário e senha, que serão encaminhados para o serviço de STS, que serão utilizadas por ele para validar o usuário (UserNameSecurityTokenHandler).

Com a factory configurada, agora podemos recorrer ao método CreateChannel, que retornará o canal de comunicação configurado com o STS. Esse método retornará o proxy, que implementará o contrato IWSTrustChannelContract. Como podemos perceber, esse contrato disponibilizará os métodos para emissão, validação e cancelamento de tokens. O método que veremos aqui é o Issue, que recebe como parâmetro a instância da classe RequestSecurityToken (RST), que parametriza alguma ação exposta pelo contrato. A configuração desta classe exige informarmos o endereço da relying party que queremos acessar (AppliesTo), que determina se o STS pode ou não emitir o token para ela. Abaixo podemos visualizar o código que comentamos acima:

private const string STS_ADDRESS = “http://IsraelAeceNB2:9010/sts”;
private const string SRV_ADDRESS = “http://IsraelAeceNB2:9000/ServicoDeCredito”;

private static SecurityToken EmitirToken()
{
    using (WSTrustChannelFactory af =
        new WSTrustChannelFactory(
            new UserNameWSTrustBinding(SecurityMode.Message),
            new EndpointAddress(
                new Uri(STS_ADDRESS),
                EndpointIdentity.CreateDnsIdentity(“Autenticador”))))
    {
        af.Credentials.ServiceCertificate.SetDefaultCertificate(
            “CN=Autenticador”, StoreLocation.LocalMachine, StoreName.My);

        af.Credentials.UserName.UserName = “Israel”;
        af.Credentials.UserName.Password = “P@ssw0rd”;

        IWSTrustChannelContract channel = af.CreateChannel();
        RequestSecurityTokenResponse response = null;

        var token = channel.Issue(new RequestSecurityToken()
        {
            RequestType = RequestTypes.Issue,
            AppliesTo = new EndpointAddress(SRV_ADDRESS)
        }, out response);

        return token;
    }
}

Por último, e não menos importante, temos o retorno do método Issue. Ele retorna a instância de uma classe que herda direta ou indiretamente da classe SecurityToken, correspondendo ao token gerado pelo serviço de STS, caso a autentição tenha resultado com sucesso. É este token que informaremos para o serviço antes de efetuar a chamada para as operações. Opcionalmente você pode capturar a mensagem de resposta do processo de autenticação, e para isso, você pode utilizar uma sobrecarga que há no método Issue, que permite especificar através de um parâmetro de saída uma classe do tipo RequestSecurityTokenResponse (RSTR), e com isso ter acesso a informações como tempo de vida, tipo de autenticação, tipo do token gerado, etc.

Com o token gerado, agora nos resta passá-lo para o serviço, para que o mesmo o utilize  para permitir a execução da operação que desejarmos invocar. Para a criação do serviço, vamos também recorrer à uma factory, definindo em seu tipo genérico o contrato do serviço. Em seu construtor, passamos a instância do binding WS2007FederationHttpBinding, que permite dialogarmos com um serviço que suporta este tipo de autenticação (via STS). Além dele, ainda é necessário informarmos o endereço até o serviço, assim como já fazemos quando desejamos invocar qualquer serviço WCF. Depois deste objeto criado, precisamos configurar alguns parâmetros que são necessários para estabelecer a ligação entre o cliente e o serviço.

Essas configurações estão todas concentradas na propriedade Credentials, que é exposta pela classe ChannelFactory<TChannel>. Só que antes de analisar as propriedades que ela fornece, precisamos entender o comportamento deste binding. Como ele exige um token para enviar ao serviço, é necessário informarmos ele antes de efetuar a requisição para qualquer operação. A propriedade SupportInteractive tem a finalidade de exibir ou não a tela do Windows Cardspace, solicitando que o usuário informe este token. No cenário deste exemplo, o token já foi emitido, devido a uma solicitação explícita ao serviço de STS, e justamente por isso, devemos evitar que essa tela seja exibida, definindo-a como False.

Em seguida, especificamos a chave pública que foi fornecida pelo serviço, garantindo que a mensagem que chegará para ele venha devidamente protegida. A propriedade CertificateValidationMode se faz necessária porque precisamos desabilitar a validação do certificado, já que ele foi criado apenas para testes. Para finalizar a configuração, invocamos o método de estensão chamado ConfigureChannelFactory, que é fornecido através da classe ChannelFactoryOperations, que por sua vez, está debaixo do namespace Microsoft.IdentityModel.Protocols.WSTrust.

Essa classe ainda fornece mais um método (de estensão) importante para o nosso exemplo, que é o CreateChannelWithIssuedToken. Este método recebe como parâmetro a instância de um token, que é representado pela classe SecurityToken, que criamos no passo acima, retornando o proxy de comunicação com o serviço. É neste momento que informarmos ao serviço o token que será utilizado para efetuar a chamada para as operações dele. O código abaixo ilustra toda essa configuração, incluindo as chamadas para as operações expostas pelo serviço.

private static void InvocarServico(SecurityToken token)
{
    using (ChannelFactory<IConsultasFinanceiras> sf =
        new ChannelFactory<IConsultasFinanceiras>(
            new WS2007FederationHttpBinding(), 
            new EndpointAddress(
                new Uri(SRV_ADDRESS),
                EndpointIdentity.CreateDnsIdentity(“Servico”))))
    {
        sf.Credentials.SupportInteractive = false;
        sf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
            X509CertificateValidationMode.PeerTrust;
        sf.Credentials.ServiceCertificate.SetDefaultCertificate(
            “CN=Servico”, StoreLocation.LocalMachine, StoreName.My);
        sf.ConfigureChannelFactory();

        var channel = sf.CreateChannelWithIssuedToken(token);
        channel.DefinirNovoLimiteDeCredito(10, 1000);
        Console.WriteLine(channel.RecuperarLimiteDeCredito(1000));
    }
}

Como podemos notar no exemplo acima, a chamada para o serviço está totalmente independente do modelo de  autenticação. Tudo o que precisa fazer é informar o token gerado pelo STS, sem ter ideia de como a autenticação foi realizada. Amanhã, o modelo de autenticação exigido pelo STS muda e o consumo do serviço não sofrerá qualquer alteração.

Conclusão: Neste artigo pudemos desvendar todo o procedimento que é reliazado internamente pelo WIF quando tentamos consumir um serviço WCF exige que a autenticação seja realizada por um STS. Agora coordenamos isso manualmente, o que nos dá certa flexibilidade, como por exemplo, em um ambiente passivo, evitar o redirecionamento para o STS para autenticar o usuário, onde poderíamos fornecer uma página local de login, e através desta alternativa, consumir o STS requisitando a emissão do token e, consequentemente, reutilizar o token durante toda a sesão do usuário.

Acessando serviços de forma relativa

Como sabemos, aplicações Silverlight podem recorrer a recursos que rodam no servidor que a hospeda utilizando WCF. Com isso, a aplicação que serve como host para o serviço é uma aplicação ASP.NET tradicional (WebForms ou MVC, Web Site ou Web Application), onde efetivamente criaremos o serviço WCF para que servirá a aplicação Silverlight.

Uma vez com o serviço pronto, tudo o que precisamos fazer para consumir o serviço no Silverlight, é a referência para o mesmo, utilizando a opção Add Service Reference, fornecida pela IDE do Visual Studio .NET. Como é feito em qualquer aplicação cliente, o Visual Studio extrairá as informações do documento WSDL e criará o proxy do lado do cliente, incluindo um arquivo chamado ServiceReferences.ClientConfig, que conterá as configurações de acesso ao serviço referenciado. O código abaixo ilustra esse arquivo:

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name=”BasicHttpBinding_IContrato”
                 maxBufferSize=”2147483647″
                 maxReceivedMessageSize=”2147483647″>
          <security mode=”None” />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address=”http://localhost:1551/ServicoDeTeste.svc&#8221;
                binding=”basicHttpBinding”
                bindingConfiguration=”BasicHttpBinding_IContrato”
                contract=”Servico.IContrato”
                name=”BasicHttpBinding_IContrato” />
    </client>
  </system.serviceModel>
</configuration>

Como podemos notar, endereço do serviço WCF está definido de forma absoluta. Isso implica em alguns problemas que podemos ter durante a distribuição/instalação do projeto no servidor. Esse arquivo é comprimido para dentro de um outro arquivo, com extensão *.xap, e que por sua vez, é depositado no diretório ClientBin do projeto ASP.NET que hospeda a aplicação Silverlight. Ao colocar isso no servidor onde ele deverá rodar, provavelmente o endereço do serviço mudará, e ficará complicado e trabalhoso para alterar.

Para melhorar isso, a versão 4.0 do Silverlight permite especificar, de forma relativa, o endereço para o serviço, ou seja, não precisamos mais definir o endereço completo até o arquivo *.svc. Apesar da IDE ainda não ser inteligente o bastante para utilizar esse recurso ao referenciar o serviço, nada nos impede de alterar isso diretamente no arquivo de configuração do lado do cliente, apontando relativamente para o serviço WCF. Para o teste, eu criei um serviço na raiz da aplicação ASP.NET, e como a aplicação Silverlight é colocada na pasta ClientBin desta mesma aplicação, tudo o que precisamos fazer é subir um nível (../). Abaixo podemos visualizar essa ligeira mudança, mas que traz uma grande flexibilidade.

<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name=”BasicHttpBinding_IContrato”
                 maxBufferSize=”2147483647″
                 maxReceivedMessageSize=”2147483647″>
          <security mode=”None” />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address=”../ServicoDeTeste.svc”
                binding=”basicHttpBinding”
                bindingConfiguration=”BasicHttpBinding_IContrato”
                contract=”Servico.IContrato”
                name=”BasicHttpBinding_IContrato” />
    </client>
  </system.serviceModel>
</configuration>

Autenticação via Claims no WCF

Nos artigos anteriores, falei sobre a proposta da Microsoft para tentar unificar o sistema de autenticação das aplicações. Nos dois primeiros artigos, comentei um pouco sobre os problemas existentes e a teoria que circunda qualquer Identity MetaSystem. Na sequência, introduzi o WIF e alguns tipos que são comuns para qualquer tipo de aplicação que fará uso deste novo framework. A finalidade deste artigo é mostrar como podemos aplicar o WIF em serviços WCF, analisando tudo o que é necessário para colocar isso em funcionamento, incluindo a criação de um STS customizado para testes.

Em um artigo anterior, vimos como podemos implementar o WIF à aplicações ASP.NET, que recorrem ao navegador para serem apresentadas. Com isso, o WIF ou qualquer outro framework de autenticação, faz uso das funcionalidades que o navegador disponibiliza, para assim, coordenar de forma passiva, tudo o que é necessário para efetuar a autenticação em uma segunda aplicação.

Quando falamos em autenticação de serviços, o processo muda um pouco, pois geralmente eles são acessados através de uma aplicação cliente, que muitas vezes deve se identificar antes de consumir as funcionalidades que ele expõe. Neste cenário podemos perceber que não há o navegador, e sim uma aplicação (na maioria das vezes, desktop (Windows Forms ou WPF)) que consomem o serviço.

No artigo anterior discutimos o ambiente passivo, e aqui entra o ambiente ativo. O nome se deve ao fato de que a aplicação cliente coordena ativamente o processo de autenticação do usuário quando ele, por sua vez, precisar acessar um serviço que exija que o usuário esteja devidamente autenticado para poder acessá-lo. Como já acontecia anteriomente, os serviços que compõem a sua estrutura, pode seguramente confiar em um orgão que efetua a autenticação, evitando assim configurarmos serviço por serviço todo o processo necessário para a autenticação do usuário.

Como o WCF já traz vários pontos de estensibilidade, a Microsoft apenas introduziu nesses locais recursos para interceptar o processo de autenticação, e com isso, acoplando o WIF para efetuar todo o trabalho. A ideia aqui é ao configurar um serviço WCF, dizer que ele somente permitirá usuários autenticados por uma outra aplicação, que também será um serviço, mas construído com os recursos fornecidos pelo WIF.

Ao expor um serviço WCF neste caso, a configuração do mesmo deverá conter entre vários itens, o autenticador. Com isso, o WIF será capaz de modificar o documento WSDL que descreve o serviço, também incluindo o local deste autenticador que o teu serviço confia. Com isso, ao referenciar o serviço em uma aplicação cliente, ela já saberá qual o endereço correto do autenticador e, consequentemente, antes de efetuar a requisição efetiva para uma das operações do serviço, ele será inteligente o bastante para autenticar o usuário no autenticador e, finalmente, invocar a operação solicitada. Através da imagem abaixo, podemos visualizar as etapas deste processo:

É importante dizer que no ambiente ativo, o processo acaba sendo mais rápido quando comparado com o ambiente passivo, pois o cliente não precisa visitar o serviço para saber qual é o autenticador que o mesmo utiliza. Isso já aconteceu durante a referência do serviço na aplicação cliente, que traz, além da descrição do serviço em si, informações inerentes ao autenticador em qual o serviço confia, assim como já foi comentado acima.

Depois de entendermos o processo em um nível macro, precisamos nos atentar em como podemos proceder para preparar tudo isso. Em primeiro lugar, vamos avaliar as opções de template de projeto voltadas para o WCF, que o WIF nos fornece quando instalamos o seu SDK:

  • WCF Security Token Service: Template que traz a configuração padrão para criar um serviço WCF que servirá como um STS no ambiente ativo.
  • Claims-aware WCF Service: Template que traz um serviço WCF pré-configurado para utilizar algum STS, que será responsável pela autenticação do usuário.

Apesar delas existirem, não vamos utilizá-las no decorrer deste artigo. A ideia aqui é apresentar os detalhes de criação do começo ao fim, analisando cada uma das seções necessárias para fazer com que todo esse processo funcione da forma correta. Com isso, teremos três elementos envolvidos no nosso exemplo: o serviço (relying party) que contém todas as funcionalidades que desejamos expor aos clientes, um autenticador (STS), que será o responsável por autenticar os usuários e, finalmente, a aplicação cliente (subjects), que utilizará o serviço WCF para extrair as informações e exibí-las na tela, mas antes disso, deverá autenticar o usuário que a acessa no STS correspondente.

A criação do STS

Para iniciar, vamos começar com a construção do STS, que será o nosso autenticador. Para isso, vamos recorrer à uma aplicação do tipo Console, que irá expor um serviço que aceitará as requisições de login, validará o usuário em algum repositório e, finalmente, devolverá para a aplicação solicitante o token gerado por ele. Como já comentei nas entrelinhas, o STS nada mais é que um serviço, que se utiliza da mesma estrutura de um serviço WCF, mas expondo um tipo de serviço diferenciado, que tem a finalidade de validar e autenticar um usuário. Para termos acesso as classes que nos permite criar este tipo de serviço, devemos referenciar na aplicação o assembly Microsoft.IdentityModel.dll, que é fornecido pelo WIF.

Se o STS nada mais é que um serviço, temos que ter uma classe que o representa, e para isso o WIF já fornece uma classe abstrata chamada SecurityTokenService, que é o nosso STS em si, fornecendo toda a estrutura necessária para a criação de um STS customizado, mas para isso, devemos sobrescrever dois métodos nas classes derivadas, a saber: GetScope e GetOutputClaimsIdentity.

O primeiro método, GetScope, nos dá a possibilidade de verificar quais são as chaves de criptografia que serão utilizadas, baseando-se no endereço da relying party. Esse método retorna uma instância da classe Scope, que contém toda a configuração necessária para gerar o retorno para a respectiva aplicação (relying party). Já o segundo método, GetOutputClaimsIdentity, é o local onde efetivamente definimos quais claims serão criadas para aquele usuário recém autenticado, e que na maioria das vezes, iremos recorrer a uma busca no repositório para extrair esses dados. Esse método retorna a instância de uma classe ClaimsIdentity, que implementa a interface IClaimsIdentity, e como sabemos, fornece uma propriedade chamada Claims, que nada mais é que uma coleção, onde podemos adicionar quantas claims desejarmos.

Como mencionei, o método GetScope é responsável por capturar os recursos necessários para estabelecer a comunicação segura. Aqui teremos dois certificados, sendo um deles utilizado pelo autenticador (STS) e outro pelo serviço (relying party). Cada um deles vai garantir que as mensagens trocadas não sejam manipuladas por ninguém. Para isso, a classe Scope fornece duas propriedades: EncryptingCredentials e SigningCredentials. Na primeira propriedade, definimos o certificado fornecido pelo serviço, que com a sua chave pública, faremos a criptografia da mensagem quando formos retornar a resposta do processo de autenticação (RSTR). Já a propriedade SigningCredentials, apontamos para o certificado do autenticador, que utilizará para garantir que a mensagem não será alterada durante a sua viagem. Abaixo temos um trecho da classe que representa o serviço de STS, omitindo algumas seções por questões de espaço:

public class ServicoDeAutenticacao : SecurityTokenService
{
    public ServicoDeAutenticacao(ConfiguradorDoServicoDeAutenticacao configurador)
        : base(configurador) { }

    protected override IClaimsIdentity GetOutputClaimsIdentity(
        IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
    {
        principal.Identities[0].Claims.AddRange(
            RepositorioDeUsuarios.RecuperarClaimsDoUsuario(principal.Identity.Name));

        return principal.Identity as IClaimsIdentity;
    }

    protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request)
    {
        Scope scope = new Scope(request.AppliesTo.Uri.AbsoluteUri);
        scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(request.AppliesTo);
        scope.SigningCredentials = new X509SigningCredentials(
            CertificateUtil.GetCertificate(
                StoreName.My,
                StoreLocation.LocalMachine,
                “CN=Autenticador”));

        return scope;
    }

    //Outros Membros
}

Depois da classe que representa o serviço de STS criada, o próximo passo é a criação de uma classe que herda da classe SecurityTokenServiceConfiguration. Essa classe permite configurar um serviço de STS, definindo qual será o tipo da classe que representará o STS, que foi aquela que criamos no passo anterior. Essa classe possui várias propriedades, que permite configurar de forma imperativa o STS, mas neste caso, tudo o que precisamos fazer é herdar, e dentro do seu construtor, definir duas principais propriedades: SecurityTokenService e SigningCredentials. A primeira correspondente ao Type da classe que herda de SecurityTokenService, enquanto a segunda, define os dados do certificado usado para a conversação. O código abaixo mostra a sua estrutura:

public class ConfiguradorDoServicoDeAutenticacao : SecurityTokenServiceConfiguration
{
    public ConfiguradorDoServicoDeAutenticacao()
        : base(“ServicoDeAutenticacao”)
    {
        this.SecurityTokenService = typeof(ServicoDeAutenticacao);
        this.SigningCredentials = new X509SigningCredentials(
            CertificateUtil.GetCertificate(
                StoreName.My,
                StoreLocation.LocalMachine,
                “CN=Autenticador”));
    }
}

Para encerrar as classes customizadas, é necessário a criação de um security token handler. Neste contexto, o handler é responsável por extrair o token enviado pelo cliente, e validá-lo em algum repositório. Como queremos optar para que o usuário informe o seu login e senha, utilizaremos aqui um security token handler exclusivo para este caso, que é o UserNameSecurityTokenHandler.

Ao herdar desta classe, devemos sobrescrever o método ValidateToken, que fornece como parâmetro a instância do token enviado pelo usuário, representado pela classe UserNameSecurityToken, que por sua vez, fornece duas propriedades pertinentes ao modelo de autenticação, que são elas: UserName e Password. O exemplo abaixo ilustra como proceder para customizar essa classe:

public class ValidadorDeUsuario : UserNameSecurityTokenHandler
{
    public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
    {
        UserNameSecurityToken usernameToken = token as UserNameSecurityToken;

        if (!RepositorioDeUsuarios.Validar(usernameToken.UserName, usernameToken.Password))
            throw new SecurityException(“Usuário inválido.”);

        ClaimsIdentity identidadeAtual = new ClaimsIdentity(“ValidadorDeUsuario”);
        identidadeAtual.Claims.Add(new Claim(ClaimTypes.Name, usernameToken.UserName));

        return new ClaimsIdentityCollection() { identidadeAtual };
    }

    //Outros Membros
}

Como podemos notar no código acima, é dentro do método ValidateToken que devemos recorrer ao repositório de usuários para validar as credenciais informadas pelo cliente. Caso o usuário não seja encontrado, você pode disparar uma exceção do tipo SecurityException, informando a inexistência daquele usuário.

E para colocar tudo isso em funcionamento, entra em cena a classe WSTrustServiceHost. Essa classe herda diretamente da classe ServiceHost (exposta pela API do WCF), que permite carregar e gerenciar um serviço STS utilizando toda a estrutura do WCF, e com isso, ficará disponível para receber as requisições das aplicações clientes que desejam autenticar seus usuários, antes de efetivamente acessar uma das operações expostas pelo serviço. Em princípio, temos que recorrer a esta classe quando não estamos utilizamos as templates de projetos que vimos acima, pois quando as usamos, isso tudo o que vemos aqui ficará transparente.

Há um overload do construtor da classe WSTrustServiceHost, que aceita como parâmetro uma classe do tipo SecurityTokenServiceConfiguration, contendo toda a configuração necessária de um serviço de STS. O código abaixo ilustra a sua utilização:

using (WSTrustServiceHost host = 
    new WSTrustServiceHost(new ConfiguradorDoServicoDeAutenticacao()))
{
    host.Open();
    Console.WriteLine(“[ Serviço de Autenticação em funcionamento ]”);
    Console.ReadLine();
}

Assim como qualquer serviço WCF, somente essa configuração não é suficiente para subir o serviço de STS. Ainda há a necessidade de apontar, via arquivo de configuração, outras informações necessárias para o mesmo, tais como endpoints, contratos, baseAddresses, etc., que como sabemos, é necessário para todo e qualquer tipo de serviço WCF. Mas como estamos criando uma espécie de serviço voltado exclusivamente para o STS, temos já contratos predefinidos, que expõem funcionalidades de emissão, cancelamento e renovação de tokens, mas tudo isso tudo será utilizado, em um primeiro momento, internamente pelo runtime do WIF.

Sendo assim, a classe que representará o serviço é a WSTrustServiceContract, que implementa a interface IWSTrust13SyncContract, que define o contrato do serviço, fornecendo as operações comentadas no parágrafo anterior. Agora o nosso serviço deverá expor dois endpoints, sendo um deles utilizando o contrato IWSTrust13SyncContract em conjunto com o binding WS2007HttpBinding, e além disso, um binding tradicional de metadados (IMetadataExchange), que irá expor todas as funcionalidades do serviço de STS. A única configuração relevante do binding, é que devemos definir o atributo clientCredentialType do elemento message como sendo UserName, já que a ideia é permitir ao usuário informar os dados de acesso através da aplicação cliente. Abaixo podemos visualizar as configurações deste serviço:

<?xml version=”1.0″?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=”Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract”
               behaviorConfiguration=”serviceConfig”>
        <host>
          <baseAddresses>
            <add baseAddress=”http://localhost:9010/sts”/&gt;
          </baseAddresses>
        </host>
        <endpoint address=””
                  binding=”ws2007HttpBinding”
                  bindingConfiguration=”bindingConfig”
                  contract=”Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract” />
        <endpoint address=”mex”
                  binding=”mexHttpBinding”
                  contract=”IMetadataExchange” />
      </service>
    </services>
    <bindings>
      <ws2007HttpBinding>
        <binding name=”bindingConfig”>
          <security mode=”Message”>
            <message clientCredentialType=”UserName”
                     negotiateServiceCredential=”false”
                     establishSecurityContext=”false”/>
          </security>
        </binding>
      </ws2007HttpBinding>
    </bindings>
</configuration>

Como se não bastasse, ainda precisamos das configurações referentes ao WIF neste mesmo arquivo de configuração da aplicação que servirá como host do serviço de STS. Como já vimos em artigos anteriores, a seção para essa configuração é a microsoft.identityModel, que deve ser registrada primeiramente antes de ser utilizada.

Depois da seção registrada, o próximo passo é remover o security token handler que é adicionado por padrão (WindowsUserNameSecurityTokenHandler), e em seguida, adicionarmos aquele que criamos acima. Para isso, recorremos à uma coleção chamada securityTokenHandlers, e especificamos ali o tipo completo, incluindo o nome e o assembly onde ele foi criado. Além disso, ainda há a seção serviceCertificate, que apontamos o certificado que o autenticador irá utilizar para se comunicar com o serviço, protegendo todas mensagens que são trocadas entre eles. O código abaixo resume todas as configurações do WIF que são necessárias na aplicação que hospedará o serviço STS, que compõem o arquivo de configuração que vimos acima com as configurações pertinentes ao WCF:

<?xml version=”1.0″?>
<configuration>
  <configSections>
    <section name=”microsoft.identityModel”
             type=”Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, …”/>
  </configSections>
  <microsoft.identityModel>
    <service>
      <securityTokenHandlers>
        <remove type=”Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, …” />
        <add type=”Autenticador.ValidadorDeUsuario, Autenticador …” />
      </securityTokenHandlers>
      <serviceCertificate>
        <certificateReference storeLocation=”LocalMachine”
                              storeName=”My”
                              x509FindType=”FindBySubjectDistinguishedName”
                              findValue=”CN=Autenticador” />
      </serviceCertificate>
    </service>
  </microsoft.identityModel>
</configuration>

A criação do Serviço

Depois do serviço de autenticação criado, vamos partir para o serviço que utilizará e confiará naquele STS para autenticar os usuários. O serviço que vamos utilizar como exemplo é bem simples, com apenas duas operações que recebem e retornam tipos simples. A ideia aqui será restringir o acesso às operações deste serviço, permitindo o acesso somente depois do usuário ser devidamente autenticado pelo serviço de STS que criamos acima.

Sendo assim, a configuração do serviço WCF com as nossas regras de negócio é simples, contendo os endpoints, os behaviors para expor os metadados e a criação de um hosting, que utilizaremos neste exemplo, uma aplicação do tipo Console. A única diferença aqui na configuração do serviço é a utilização do binding WS2007FederationHttpBinding, que é utilizado quando você está terceirizando o processo de autenticação.

No código abaixo podemos visualizar a configuração do binding do endpoint do nosso serviço. Definimos a segurança como sendo Message, que garantirá que a mensagem seja trafegada de forma segura, mesmo que o transporte não forneça esse recurso. Nas configurações internas, temos um elemento chamado issuerMetadata, que através do atributo address, devemos especificar o endereço do endpoint do serviço de STS, responsável pela autenticação. Em segundo lugar e não menos importante, temos o elemento claimTypeRequirements, que é uma coleção onde podemos especificar quais são as claims que o serviço está exigindo que o STS emita.

<bindings>
  <ws2007FederationHttpBinding>
    <binding name=”bindingConfig”>
      <security mode=”Message”>
        <message>
          <issuerMetadata address=”http://localhost:9010/sts/mex&#8221; />
          <claimTypeRequirements>
            <add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/name&#8221;
                 isOptional=”false” />
            <add claimType=”http://schemas.microsoft.com/ws/2008/06/identity/claims/role&#8221;
                 isOptional=”false” />
          </claimTypeRequirements>
        </message>
      </security>
    </binding>
  </ws2007FederationHttpBinding>
</bindings>

No serviço também precisamos de algumas configurações pertinentes ao WIF, e como já era de se esperar, é necessário também registrar a seção do mesmo, através do elemento configSections. Neste caso, precisamos definir a audienceUri, que nos permite elencar uma coleção de URIs, que definem os possíveis endereços para qual o token poderá ser entregue, evitando que clientes maliciosos façam uso do mesmo. Em seguida temos a seção serviceCertificate, que apontamos o certificado que o serviço vai utilizar para se comunicar com o autenticador, protegendo as mensagens que são trocadas entre eles. Abaixo o código que ilustra essas configurações:

<?xml version=”1.0″?>
<configuration>
  <configSections>
    <section name=”microsoft.identityModel”
             type=”Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection …”/>
  </configSections>
  <microsoft.identityModel>
    <service>
      <issuerNameRegistry type=”Servico.ValidadorDoAutenticador, Servico …” />
      <audienceUris>
        <add value=”http://localhost:9000/ServicoDeCredito&#8221; />
      </audienceUris>
      <serviceCertificate>
        <certificateReference storeLocation=”LocalMachine”
                              storeName=”My”
                              x509FindType=”FindBySubjectDistinguishedName”
                              findValue=”CN=Servico” />
      </serviceCertificate>
    </service>
  </microsoft.identityModel>
</configuration>

Finalmente, ainda analisando o código acima, temos um elemento chamado issuerNameRegistry, que como o próprio nome diz, permite especificarmos ali os STSs em que a aplicação confia. Para termos o controle desta validação, podemos criar uma classe que herde da classe abstrata IssuerNameRegistry, sobrescrevendo o método GetIssuerName, analisando o certificado fornecido pelo autenticador, e assim tomarmos alguma decisão para saber se confiamos ou não nele. Se iremos confiar, então devemos retornar uma string representando o autenticador, caso contrário, devemos disparar uma exceção para reportar essa “falha”. O código abaixo ilustra essa classe:

public class ValidadorDoAutenticador : IssuerNameRegistry
{
    public override string GetIssuerName(SecurityToken securityToken)
    {
        X509SecurityToken certificado = securityToken as X509SecurityToken;

        if (certificado != null)
            if (certificado.Certificate.SubjectName.Name == “CN=Autenticador”)
                return certificado.Certificate.SubjectName.Name;

        throw new SecurityTokenException(“Autenticador Inválido.”);
    }
}

O último detalhe é a criação do host, que quando utilizamos um self-hosted, recorrermos à classe ServiceHost, e aqui não será diferente. Só há uma pequena configuração a ser realizada aqui, que é a integração do runtime do WIF ao pipeline do WCF. Essa configuração pode ser feita de forma imperativa ou declarativa. Utilizando a primeira opção, antes de abrir o host, devemos enviar a instância da classe ServiceHost para o método estático ConfigureServiceHost, exposto pela classe FederatedServiceCredentials, assim como podemos notar no trecho de código abaixo:

using (ServiceHost host = new ServiceHost(typeof(ServicoDeCredito), new Uri[] { }))
{
    FederatedServiceCredentials.ConfigureServiceHost(host);

    host.Open();
    Console.WriteLine(“[ Serviço de Crédito em funcionamento ]”);
    Console.ReadLine();
}

Ao submeter o host para o método ConfigureServiceHost, uma série de elementos são adicionados ao processamento das requisições que chegam para o serviço, efetuando toda a comunicação necessária com o serviço de STS em qual o serviço confia, e além disso, é capaz de capturar o resultado do processo de autenticação realizado remotamente, e com isso prosseguir com o processo de autorização, que determinará o que o usuário poderá ou não acessar.

A autorização continua sendo de responsabilidade do serviço local, que na maioria das vezes recorre às roles para refinar o acesso. Quando estamos trabalhando com o WIF, todas as informações que ele emite para um determinado usuário autenticado, é representado por uma claim. No nosso exemplo, o STS está gerando três claims, sendo uma delas do tipo name, e as outras duas do tipo role. O WCF em conjunto com o WIF são capazes de analisar as claims geradas pelo STS, e depois disso, configurar a propriedade CurrentPrincipal da classe Thread com uma instância da classe ClaimsPrincipal, representando a identidade do usuário e suas respectivas claims.

Desta forma, podemos continuar trabalhando com o modelo de programação da mesma forma que já utilizávamos no passado, ou seja, através do método IsInRole (imperativo) ou com o uso do atributo PrincipalPermissionAttribute (declarativo), assim como é exemplificado abaixo:

public class ServicoDeCredito : IConsultasFinanceiras
{
    [PrincipalPermission(SecurityAction.Demand, Role = “Funcionario”)]
    public decimal RecuperarLimiteDeCredito(int codigoDoCliente)
    {
        //Implementação
    }

    [PrincipalPermission(SecurityAction.Demand, Role = “Gerente”)]
    public void DefinirNovoLimiteDeCredito(int codigoDoCliente, decimal novoValor)
    {
        //Implementação
    }
}

A criação do Cliente

Finalmente, depois dos dois serviços devidamente implementados, chega o momento de consumí-los em uma aplicação cliente. Tudo o que precisamos fazer nela, é adicionar a referência para o serviço WCF, assim como já fazemos. Não há necessidade de fazer referência ao serviço de STS na aplicação cliente, pois além do documento WSDL descrever as operações do serviço, ele também traz todas as informações necessárias a respeito do serviço responsável pela autenticação, como o endereço onde ele está localizado, quais são as claims que o serviço exige que o autenticador emita, etc.

Ao referenciar o serviço através da opção Add Service Reference da IDE do Visual Studio ou através do utilitário svcutil.exe, o proxy gerado conterá as informações tradicionais do endpoint do serviço com o qual a aplicação quer se comunicar, mas também incluirá algumas configurações extras, que determinam como a comunicação com o STS será realizada. Justamente por conhecer onde o STS está e como devemos ser comunicar com ele, que o ambiente ativo é bem mais rápido que o passivo, como já discutido anteriormente.

Outro detalhe importante no arquivo de configuração, são as chaves públicas dos certificados envolvidos no processo. Uma delas foi fornecida pelo serviço e a outra pelo autenticador, que o runtime do WCF e do WIF irão utilizá-las durante o processo de comunicação para garantir com que as mensagens que serão trocadas entre as partes sejam protegidas.

Por fim, a aplicação cliente só precisa consumir o serviço, apenas se atentando a informar o login e senha antes de invocar alguma das operações. O código abaixo ilustra a utilização do serviço, através do proxy que foi criado durante a referência do serviço. O arquivo de configuração do cliente será omitido aqui por questões de espaço.

using (ConsultasFinanceirasClient proxy = new ConsultasFinanceirasClient())
{
    proxy.ClientCredentials.UserName.UserName = “Israel”;
    proxy.ClientCredentials.UserName.Password = “P@ssw0rd”;

    proxy.DefinirNovoLimiteDeCredito(560, 10000);

    Console.WriteLine(“Limite para o cliente 200: {0:C2}”,
        proxy.RecuperarLimiteDeCredito(200));
}

Conclusão: Depois de tudo o que fizemos aqui, o nosso serviço ficou completamente independente de como se faz a autenticação do usuário. Se tivermos um barramento de serviços dentro de algum local, como uma empresa, todos os serviços podem confiar em um único autenticador, e a reutilização será muito grande, já que temos todo o processo centralizado, facilitando a manutenção, já que os serviços consumidores nada sabem sobre a forma como estamos autenticando os usuários.

TesteComAmbienteAtivo.zip (42.08 kb)

Influencia do maxRequestLength no WCF

Como eu comentei neste outro artigo, podemos utilizar no WCF um recurso chamado streaming, que é uma alternativa interessante quando precisamos enviar grandes quantidades de informações, que nestes cenários, o mais comum é o envio ou o recebimento de arquivos. Para que tudo isso funcione, precisamos nos atentar a efetuar algumas configurações no WCF com relação as cotas e timeouts, que impõem limites durante o tráfego das informações.

Em princípio, esses são os únicos cuidados que devemos ter. Poderemos começar a ter outros problemas, se esse serviço for hospedado no IIS. Para que fosse possível hospedar um serviço WCF no IIS, a Microsoft utilizou a estrutura do pipeline do ASP.NET para receber e desviar a requisição para serviços WCF para o seu respectivo handler. Antes de qualquer análise das cotas que configuramos para o serviço, há um detalhe importante que devemos nos preocupar. Trata-se da propriedade MaxRequestLength da classe HttpRuntime, que determina a quantidade máxima (em KBytes) de conteúdo que uma requisição do ASP.NET poderá receber, onde o seu valor padrão é 4 MB. Essa propriedade influencia quando queremos efetuar o upload de um arquivo grande através de uma aplicação ASP.NET.

Como a requisição chega para o IIS, que por sua vez delega o processamento para a estrutura do ASP.NET, o mesmo valida o tamanho deste conteúdo, e se for maior do que o valor estipulado nesta propriedade, você receberá uma exceção do tipo HttpException, com a seguinte mensagem: Maximum request length exceeded. É algo até complicado de se diagnosticar, já que essa exceção não é propagada para o cliente que consome o serviço, e o tracing do WCF neste caso, ajudará a desvendar este problema. De qualquer forma, quando for hospedar um serviço WCF no IIS, é importante que você sincronize as cotas com o atributo maxRequestLength exposto pelo elemento httpRuntime, assim como podemos notar no exemplo abaixo, que está configurado para receber um arquivo de até 100 MB:

<?xml version=”1.0″?>
<configuration>
  <system.web>
    <httpRuntime maxRequestLength=”102400″/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name=”Service”>
        <endpoint address=””
                  binding=”basicHttpBinding”
                  contract=”IService”
                  bindingConfiguration=”config” />
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name=”config”
                 messageEncoding=”Mtom”
                 transferMode=”Streamed”
                 maxBufferSize=”104857600″
                 maxBufferPoolSize=”104857600″
                 maxReceivedMessageSize=”104857600″>
        </binding>
      </basicHttpBinding>
    </bindings>
</configuration>

Acessando ações diretamente

Em algumas situações dentro de uma aplicação ASP.NET MVC, há ações dentro dos controllers que não podem ser invocadas diretamente a partir da URL, ou seja, somente estarão acessíveis através de métodos que geram o resultado (HTML) “in-line”, ou seja, em algum ponto da página. Um exemplo disso é o uso dos métodos Action e RenderAction.

Como exemplo, há métodos que recebem como parâmetro o nome de uma ação e, eventualmente, o nome do controller, e o resultado irá variar de acordo com o método que você utiliza. Um desses métodos, chamado de Action, retornará um objeto do tipo MvcHtmlString, que colocará o resultado diretamente no local especificado dentro do HTML da página; enquanto o método RenderAction adiciona o conteúdo diretamente no objeto HttpResponse, que em alguns cenários, pode ser mais performático.

Muitas vezes, estas ações devem ser acessadas somente através destes métodos, e com isso um usuário não deveria acessá-la diretamente através do navegador. Para evitar que isso aconteça, você pode recorrer a um novo atributo, incluído na versão 2.0 do ASP.NET MVC, chamado ChildActionOnlyAttribute. Este atributo pode ser aplicado tanto em classes quanto em métodos, e quando o runtime do ASP.NET encontrá-lo, irá negar o acesso à mesma caso esteja sendo acessada diretamente através navegador. Para exemplificar, considere o controller abaixo, que gerencia os usuários do sistema:

public class UsuariosController : Controller
{
    public ActionResult Cadastrar()
    {
        return View();
    }

    [ChildActionOnly]
    public ActionResult VerificarExistenciaDoUsuario(string nome)
    {
        return PartialView();
    }
}

Agora, no código HTML da página ASPX da ação Cadastrar, podemos utilizar um dos métodos que falamos acima, especificando o nome da ação marcada com o atributo tema deste artigo, e com isso, o resultado gerado será colocado no mesmo local onde as tags <% %> estão localizadas. Abaixo podemos visualizar a sua utilização:

   

Note que ao navegar pela aplicação através do endereço Usuarios/Cadastrar, visualizamos o conteúdo normalmente, mas se tentarmos acessar o endereço Usuarios/VerificarExistenciaDoUsuario, a seguinte mensagem é exibida: The action ‘VerificarExistenciaDoUsuario’ is accessible only by child request. Mensagem essa, que não aconteceria se você omitir o atributo ChildActionOnlyAttribute.

Além da forma declarativa que vimos acima, você pode controlar se a chamada está ou não sendo realizada diretamente através do navegador. Para isso, basta recorrermos à propriedade IsChildAction da classe ControllerContext, que retorna um valor boleano indicando se ela está ou não sendo acessada diretamente. Agora, podemos tomar alguma decisão em cima desta propriedade, algo que é impossível de realizar quando utilizamos o modo declarativo.

Silverlight e a chamada assíncrona de serviços

Em geral, podemos consumir os serviços WCF de duas formas: síncrona ou assíncrona. Na primeira opção, ao referenciar o serviço em uma aplicação cliente e invocar um das operações que ele disponibiliza, a comunicação será realizada e enquanto ela não retorna, a aplicação ficará bloqueada aguardando o resultado. Já no segundo modelo, ao realizar a chamada de forma assíncrona, a operação será realizada em um thread secundária, permitindo que a aplicação continue trabalhando enquanto o serviço é executado.

Como sabemos, o Silverlight tem cada vez mais espaço como sendo front-end de aplicações. Essa tecnologia recorre à serviços quando precisa buscar algum conteúdo remoto, como por exemplo, preencher os dados em um controle ListBox quando um botão for pressionado. Atualmente, a maioria desses serviços são construídos em WCF, e referenciados na aplicação Silverlight para permitir que a mesma interaja com o servidor para extrair os dados necessários para executar o seu trabalho.

Para aqueles que já utilizam essa técnica, já devem ter percebido que no Silverlight, só podemos consumir esses serviços de forma assíncrona, ou seja, recorrendo à uma segunda thread através do par de métodos BeginXXX/EndXXX ou através de eventos. Mas porque isso acontece ou porque é necessário?

O Silverlight possui apenas uma única thread, que é chamada de UI Thread, e como sabemos, operações de I/O bound, como é o caso da comunicação através da rede, são tarefas custosas e que podem levar um grande tempo para ser executada, e justamente por isso, se o Silverlight bloqueasse a thread de UI enquanto executa essa requisição, o host (que é o navegador), também seria bloqueado. Mesmo que você tente emular uma chamada síncrona, utilizando algum recurso primitivo de sincronização (como o ManualResetEvent), você teria problemas do mesmo jeito, já que quando invocar o método WaitOne desta classe, ele bloqueará a thread de UI para esperar o resultado, que nunca chegará, pois o resultado somente será entregue para o Silverlight quando a thread de UI não estiver executando nenhum código, que não é o caso aqui, e como já era de se esperar, teremos um deadlock.

Desde o proxy de serviços WCF até classes de baixo nível, como é o caso da WebClient, terão o mesmo comportamento, ou seja, deverão ser sempre acionadas através do modelo assíncrono. Tudo isso se deve ao fato de que os navegadores atuais implementam a NPAPI (Netscape Plugin Application Programming Interface). A NPAPI trata-se de uma API multi-plataforma desenvolvida pela Netscape que permite que plugins sejam utilizados dentro dos navegadores. Para que os plugins sejam considerados multi-plataforma, eles precisam seguir rigorosamente essa API, o que determina que métodos remotos sejam executados assincronamente, e como já percebemos, o Silverlight segue o que foi definido por ela.

Detectando mudanças em objetos

Há uma porção de funcionalidades dentro do .NET Framework, que podemos incorporar em nossos tipos, para enriqucê-los ainda mais em nível de comportamento. Essas funcionalidades predefinidas, já trazem recursos extremamente interessantes, que com pouco de código extra que utilizamos para “rechear” os pontos customizados, poderemos poupar muitas e muitas linhas de código se fossemos fazer isso manualmente.

Entre as várias funcionalidades que existem e que já falei bastante por aqui, uma delas é a capacidade que temos de dectectar mudanças que acontecem no estado (propriedades) das nossas classes. Geralmente as classes possuem propriedades que podem ser alteradas durante a sua execução, sendo essa alteração realizada através do bloco Set da mesma, ou através de algum método que a manipula internamente.

Por algum motivo, se quisermos detectar que alguma mudança está acontecendo ou já aconteceu, podemos implementar nesta classe as interfaces INotifyPropertyChanging e INotifyPropertyChanged que estão debaixo do namespace System.ComponentModel. Cada uma delas é utilizada para interceptar momentos diferentes, ou seja, a primeira deve ser utilizada quando queremos ser notificados antes da mudança, enquanto a segunda deverá ser utilizada para notificar quando a mudança já aconteceu.

A primeira interface, INotifyPropertyChanging, fornece um único membro, que é o evento PropertyChanging. Já a segunda, INotifyPropertyChanged, disponibiliza um outro evento chamado PropertyChanged. Ambas interfaces podem ser implementadas em classes que você deseja monitorar as alterações que podem acontecer em suas respectivas propriedades. Com isso, podemos permitir aos consumidores desta classe, serem notificados antes e depois da mudança acontecer, podendo assim tomar alguma decisão em cima disso.

Como podemos notar no código abaixo, temos uma classe chamada Cliente, que por sua vez, contém apenas uma única propriedade chamada Nome. Para qualquer evento que você crie, o ideal é criar um método que encapsule a regra para a construção dos parâmetros e o disparo dele, para evitar redundâncias pelo código. Para isso, foram criados dois métodos auxiliares, onde cada um deles encapsula a chamada para o evento que ele gerencia. Note que a atribuição do valor ao membro interno da classe, somente se dá caso o valor que chega para ela seja diferente do qual ela possui, justamente porque não faz sentido notificar alguém que a propriedade foi mudada, mas que efetivamente não foi. É importante notar também que a alteração que será feita na propriedade está envolvida pela chamada dos eventos, ou seja, antes da alteração disparamos o evento PropertyChanging, e depois que a alteração foi realizada, disparamos o evento PropertyChanged.

public class Cliente : INotifyPropertyChanging, INotifyPropertyChanged
{
    private string _nome;

    public string Nome
    {
        get
        {
            return this._nome;
        }
        set
        {
            if (value != this._nome)
            {
                this.OnPropertyChanging(“Nome”);
                this._nome = value;
                this.OnPropertyChanged(“Nome”);
            }
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanging(string propertyName)
    {
        if (this.PropertyChanging != null)
            this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Com isso, ao construir um objeto do tipo Cliente, definimos a sua propriedade Nome como “Israel”. Em seguida, nos vinculamos aos eventos PropertyChanging e PropertyChanged, para sermos notificados caso qualquer alteração aconteça nas propriedades deste objeto. Tudo o que estamos fazendo no código abaixo, é escrevendo na tela a notificação da alteração da propriedade Nome. Finalmente, quando alteramos a propriedade Nome de “Israel” para “Israel Aece”, notamos que as mensagens de notificação aparecerão na tela.

Cliente c = new Cliente() { Nome = “Israel” };

c.PropertyChanging += 
    (sender, e) => Console.WriteLine(“Alterando a propriedade ‘{0}’.”, e.PropertyName);

c.PropertyChanged +=
    (sender, e) => Console.WriteLine(“Propriedade ‘{0}’ alterada.”, e.PropertyName);

c.Nome = “Israel Aece”;

Assim como existe essas interfaces que agregam às nossas classes, a possibilidade de serem monitoradas quando alguma mudança acontecer, também há uma nova interface chamada INotifyCollectionChanged (namespace System.Collections.Specialized), que como o nome sugere, permite adicionar à uma coleção, a possibilidade de monitoramento da mesma, onde seremos notificados quando ela for modificada, ou seja, quando elementos forem adicionados ou removidos. Essa interface fornece um único membro, que é o evento CollectionChanged.

Esse evento faz uso de um parâmetro do tipo NotifyCollectionChangedEventArgs, que expõe algumas propriedades interessantes, como por exemplo: Action, NewItems e OldItems. O primeiro deles, retorna uma das opções do enumerador NotifyCollectionChangedAction, dizendo qual foi a ação que aconteceu. Já a propriedade NewsItems, disponiliza um objeto do tipo IList, contendo os novos itens que foram adicionados, enquanto a propriedade OldItens, retorna o mesmo tipo da propriedade anterior, mas com os objetos que foram removidos da coleção.

Essa interface nos permite criar uma coleção que pode ser monitorada quando os elementos dela são manipulados. Felizmente, a Microsoft já adicionou uma coleção genérica chamada ObservableCollection<T>, que está debaixo do namespace System.Collections.ObjectModel, já implementada com essa funcionalidade.

É importante dizer que quando estamos falando no monitoramento de modificações em uma coleção, estamos atentos aos itens que ela armazena, se novos são inseridos, se outros são removidos, alterados, etc. Se uma propriedade de um objeto que está dentro dela for alterada, nada acontecerá à coleção. Como exemplo de uso, podemos visualizar o código abaixo, que cria uma instância desta coleção, definindo o tipo T como sendo Cliente. Antes de começarmos a manipular a coleção, vamos nos vincular ao evento CollectionChanged, para sermos notificados quando novos itens forem adicionados ou removidos.

static void Main(string[] args)
{
    ObservableCollection<Cliente> clientes = new ObservableCollection<Cliente>();
    clientes.CollectionChanged += ColecaoAlterada;

    clientes.Add(new Cliente() { Nome = “Israel” });
    clientes.Add(new Cliente() { Nome = “Claudia” });
    clientes.Add(new Cliente() { Nome = “Virginia” });
}

static void ColecaoAlterada(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
        VisualizarClientes(“Cliente(s) Adicionado(s)”, e.NewItems);

    if (e.Action == NotifyCollectionChangedAction.Remove)
        VisualizarClientes(“Cliente(s) Removido(s)”, e.OldItems);
}

static void VisualizarClientes(string titulo, IList itens)
{
    if (itens != null && itens.Count > 0)
    {
        Console.WriteLine(titulo);

        foreach (var item in itens)
            Console.WriteLine(“t{0}”, item);
    }
}

Ao executar o código acima, o método ColecaoAlterada será disparado três vezes, sendo uma para cada novo cliente que está sendo adicionado à coleção. Apesar de não estar sendo utilizado aqui, a classe ObservableCollection<T> também implementa a interface INotifyPropertyChanged, que tem a finalidade de monitorar duas propriedades, sendo elas: Items e Count, que são as principais propriedades de qualquer coleção.

Observação: Inicialmente os tipos que vimos aqui para monitoramento de coleções, foram criados para atender ao Windows Presentation Foundation, e justamente por isso, também podem ser encontrados dentro do assembly WindowsBase.dll. A partir da versão 4.0 do .NET Framework, a Microsoft trouxe esses tipos para dentro do assembly System.dll, que nos permite utilizá-los por qualquer tipo de aplicação, sem a necessidade de referenciar diretamente um assembly de uma tecnologia específica.

Conclusão: Vimos no decorrer deste artigo como podemos incorporar em nossas classes a funcionalidade para detectar mudanças, recorrendo à recursos do próprio .NET Framework. Grande parte do que vimos neste artigo, serve como base para grandes funcionalidades que estão espalhados por toda a plataforma .NET, como é o caso do databinding do WPF, consumo de serviços WCF (Data Services), entre outros. A ideia aqui foi apresentar, de uma forma “crua”, essas funcionalidades, para mais tarde abordar outros recursos expostos pelo .NET Framework que fazem uso disso, e que já assumirão o conhecimento que vimos aqui.

Feedback do Evento Codificando.NET

Como eu comentei aqui, eu palestrei no evento do Codificando.NET 2010 sobre a construção de serviços em WCF. O evento foi bem organizado, onde o Alexandre Tarifa, Andrey Sanches e o Diego Nogare se esforçaram para que o evento fosse um grande sucesso. Foi bom reencontrar esses amigos, que além deles, também falei com o Claudenir Andrade, Helio Sá Moreira, Alfred Myers e o Rodolfo Roim. Gostaria também de agradecer aqueles que ficaram para acompanhar a minha palestra, que se iniciou às 16:30hs. Para os interessados, o exemplo que montei pode ser baixado neste endereço.

Além deste evento, também aconteceu uma espécie de “micro-evento”, pois o Alexandre Tarifa forneceu um espaço para o pessoal do grupo do DotNetArchitects realizar uma reunião. Foi legal participar e poder conhecer alguns nomes importantes da comunidade, como é o caso do Fabio Margarito, Leandro Daniel, Daniel Castro, Victor Cavalcante e Vinicius Quaiato, onde em uma conversa informal foi discutido muitos assuntos pertinentes ao desenvolvimento de software.

Consumindo serviços REST com HttpClient

Como mencionei no último artigo, serviços expostos através do modelo REST não expõem o documento WSDL, usado para descrever as funcionalidades dos serviços. Sem esse tipo de documento, força os desenvolvedores a efetuar a requisição e recuperar a resposta utilizando as classes de baixo nível, tais como HttpWebRequest e HttpWebResponse, que estão dentro do namespace System.Net.

Apesar de funcionar, certas operações são extremamente difíceis de realizar. Se a requisição for feita via GET, então acaba sendo mais simples, mas tudo fica mais difícil quando é exigido que algo seja passado no corpo da mensagem, como é o caso do POST. Isso nos obriga a efetuar a construção do corpo de forma manual, sem muitos auxiliadores para ajudar nessa tarefa árdua.

Quando referenciamos um serviço em uma aplicação, o Visual Studio utiliza o documento WSDL para criar o proxy, e com isso, o consumo do serviço fica extremamente simples, já que invocamos seus respectivos métodos, como se fossem métodos locais, mas que durante a execução são encaminhados para o serviço remoto, abstraindo toda a necessidade de conhecer os detalhes do protocolo que está sendo utilizando. Como isso não é possível com serviços REST, a Microsoft incluiu no WCF-REST Starter Kit, uma biblioteca chamada HttpClient. Essa biblioteca traz várias funcionalidades para tornar o consumo de serviços REST dentro de aplicações construídas em .NET bem mais simples do que trabalhar diretamente com a classes de baixo nível que foram mencionadas acima. A finalidade deste artigo é explorar algumas dessas funcionalidades.

Ao instalar o WCF-REST Starter Kit, podemos reparar que no diretório da instalação há dois assemblies: Microsoft.Http.dll e Microsoft.Http.Extensions.dll. Esses assemblies contém todos os recursos que iremos utilizar no decorrer deste artigo, sendo o primeiro, aquele que contém todas as principais funcionalidades para efetuar a comunicação com serviços REST, enquanto o segundo, traz algumas extensões para facilitar algumas tarefas que serão bastante rotineiras durante o consumo destes serviços.

Antes de efetivamente comerçarmos a falar sobre essas classes, vamos analisar quais são as operações que o serviço está expondo. Através da interface abaixo, podemos reparar que há apenas dois métodos, onde o primeiro recebe como parâmetro uma instância da classe Usuario, e retorna uma string contendo a mensagem de sucesso ou falha da adição deste usuário em algum repositório. Já o segundo método, retorna um array, onde cada elemento é representado pela classe Usuario, contendo todos os usuários cadastrados em um determinado repositório. A classe Usuario também é extremamente simples, pois contém apenas duas propriedades: Nome e Codigo, do tipo string e inteiro, respectivamente.

[ServiceContract(Namespace = “http://www.israelaece.com/servicos&#8221;)]
public interface IUsuarios
{
    [WebInvoke]
    string Adicionar(Usuario usuario);

    [WebGet]
    Usuario[] RecuperarUsuarios();
}

Depois do serviço exposto para ser consumido através do modelo REST (WebHttpBinding), resta nos criar uma aplicação para consumí-lo, para que assim vejamos a biblioteca que é tema deste artigo em ação. Depois de criado a aplicação que irá consumir o serviço REST, devemos referenciar os dois assemblies (DLLs) que vimos acima, fornecidos pelo WCF-REST Starter Kit.

Antes de efetivamente consumirmos o serviço, é necessário saber como as mensagens serão trocadas, ou melhor, como elas devem ser formatadas. Como dito no artigo anterior, podemos recorrer à duas formas de expor as funcionalidades de serviços REST, e com aquelas informações será possível a saber quais as funcionalidades e como as mensagens devem ser trocadas. De acordo com a configuração que definimos no contrato do serviço, ele pode ser acessado utilizando o formato Xml. Ainda analisando a página gerada automaticamente pelo WCF-REST Starter Kit, podemos visualizar o schema da mensagem ou um exemplo, de como o corpo deve ser formatado para enviar ou como ele é formatado na recepção da resposta. Com isso, a mensagem para a primeira operação deve ter o corpo formatado da seguinte forma:

<xs:schema
  elementFormDefault=”qualified”
  targetNamespace=”http://www.israelaece.com/servicos”&gt;
  <xs:complexType name=”Usuario”>
    <xs:sequence>
      <xs:element minOccurs=”0″ name=”Codigo” type=”xs:int” />
      <xs:element minOccurs=”0″ name=”Nome” nillable=”true” type=”xs:string” />
    </xs:sequence>
  </xs:complexType>
  <xs:element name=”Usuario” nillable=”true” type=”tns:Usuario” />
</xs:schema>

Repare que o corpo deve conter um elemento que representará a instância da classe Usuario, com as respectivas propriedades preenchidas. Se analisarmos agora o mesmo documento para a segunda operação, veremos que ele será ligeiramente diferente, já que ela retornará uma coleção de elementos do tipo Usuario:

<xs:schema
  elementFormDefault=”qualified”
  targetNamespace=”http://www.israelaece.com/servicos”&gt;
  <xs:complexType name=”ArrayOfUsuario”>
    <xs:sequence>
      <xs:element
        minOccurs=”0″
        maxOccurs=”unbounded”
        name=”Usuario”
        nillable=”true”
        type=”tns:Usuario” />
    </xs:sequence>
  </xs:complexType>
  <xs:element name=”ArrayOfUsuario” nillable=”true” type=”tns:ArrayOfUsuario” />
    <xs:complexType name=”Usuario”>
      <xs:sequence>
        <xs:element minOccurs=”0″ name=”Codigo” type=”xs:int” />
        <xs:element minOccurs=”0″ name=”Nome” nillable=”true” type=”xs:string” />
      </xs:sequence>
    </xs:complexType>
  <xs:element name=”Usuario” nillable=”true” type=”tns:Usuario” />
</xs:schema>

Ao contrário do que acontece com o modelo SOAP, onde o Visual Studio é capaz de analisar o documento WSDL e reconstruir os tipos do lado do cliente, no modelo REST isso não será possível, assim como já foi discutido. Com isso, é necessário fazermos isso manualmente do lado do cliente. Analisando os dois códigos acima, podemos perceber que é necessário a criação de duas classes, sendo uma delas para representar a classe Usuario, enquanto a segunda, representará a coleção de usuários. Com isso a nossa classe Usuario, do lado do cliente, deve seguir o seguinte formato:

[DataContract(Namespace = “http://www.israelaece.com/servicos&#8221;)]
public class Usuario
{
    [DataMember]
    public int Codigo { get; set; }

    [DataMember]
    public string Nome { get; set; }
}

Além da classe Usuario que precisamos reconstruir, ainda precisamos nos atentar no retorno do método RecuperarUsuarios, que retorna um array, onde cada elemento deste objeto será representado pela classe criada acima. Para representar esse array contendo os usuários, podemos criar uma classe que herda da lista genérica List<T>, onde podemos substituir o parâmetro genérico T por Usuario. Além disso, ainda precisamos “mapear” o Xml correspondente à resposta para este tipo. Na formatação do Xml que vimos acima, podemos reparar que os usuários serão retornados dentro de um elemento complexo chamado ArrayOfXXX, onde XXX representa o tipo que está no interior daquela coleção. Para configurar esse “mapeamento”, utilizaremos o atributo CollectionDataContractAttribute, que podemos coordenar como a serialização/deserialização será realizada.

[CollectionDataContract(
    Name = “ArrayOfUsuario”,
    Namespace = “http://www.israelaece.com/servicos&#8221;)]
public class ColecaoDeUsuarios : List<Usuario> { }

Depois da reconstrução dos tipos que serão utilizados para representar os tipos que o serviço trabalha, vamos começar a analisar as classes que são fornecidas pela biblioteca tema deste artigo. Para inicializar, a primeira classe que vamos analisar é a HttpClient. Esta classe, que está debaixo do namespace Microsoft.Http, é responsável por gerenciar toda a comunicação com um determinado serviço. Em seu construtor, recebe o endereço base até o serviço, pois se ele fornecer mais do que uma operação, você pode reutilizar a mesma classe (HttpClient) para executar as requisições.

Seguindo o exemplo do serviço que foi criado acima, a primeira operação que vamos consumir é a Adicionar, que recebe uma instância da classe Usuario como parâmetro e retorna uma string contendo o resultado. Como já era de se esperar, existe uma classe que representa a requisição, chamada de HttpRequestMessage. Essa classe, em seu construtor, recebe uma string contendo o método HTTP que a requisição deverá ser executada. Além disso, ainda temos que informar o nome da operação a ser executada. É importante dizer que essa classe implementa a interface IDisposable, que faz a limpeza explícita dos recursos que ela utiliza, e a boa prática é envolvê-la em um bloco using. O código abaixo ilustra o uso dessas classes:

using (HttpClient http = new HttpClient(“http://localhost:1572/ServicoDeUsuarios.svc/&#8221;))
{
    using (HttpRequestMessage request = new HttpRequestMessage(“POST”, “Adicionar”))
    {
        Usuario u = new Usuario() { Codigo = 123, Nome = “Israel” };
        request.Content = HttpContentExtensions.CreateDataContract<Usuario>(u);

        using (HttpResponseMessage response = http.Send(request))
        {
            response.EnsureStatusIsSuccessful();
            Console.WriteLine(response.Content.ReadAsXElement().Value);
        }
    }
}

Ainda analisando o código que faz a requisição à operação Adicionar, podemos perceber que após criado a instância da classe que representa a requisição, precisamos definir o corpo da mesma, que deve ser representado pela classe HttpContent. Como sabemos, a operação deve receber a instância da classe Usuario, mas serializada em um formato específico. Para nos auxiliar, há uma classe estática chamada HttpContentExtensions, que fornece uma porção de métodos genéricos, que dado um tipo e seu respectivo valor, retorna uma instância da classe HttpContent, com aquele objeto formatado. Os métodos que esta classe fornece são:

  • CreateAtom10SyndicationFeed: Retorna um objeto em formato Atom 1.0. Mais detalhes neste artigo.
  • CreateDataContract: Retorna um objeto serializado em formato Xml, respeitando as regras impostos pelo serializador do WCF.
  • CreateJsonDataContract: Retorna um objeto serializado no formato Json.
  • CreateRss20SyndicationFeed: Retorna um objeto em formato RSS 2.0. Mais detalhes neste artigo.
  • CreateXmlSeriliazable: Retorna um objeto serializado utilizando o serializador Xml do .NET (XmlSerializer).

O retorno de um destes métodos deve ser atributo à propriedade Content da classe HttpRequestMessage, assim como notamos no código acima. Depois disso, vamos recorrer à instância da classe HttpClient para enviar a requisição ao respectivo serviço. Essa classe fornece vários métodos autoexplicativos: Get, Post, Put e Delete. Cada um desses métodos recebe, individualmente, parâmetros como Uri, HttpContent, etc., mas internamente, todos recorrem ao método Send, que encapsula a criação do objeto HttpRequestMessage. O código abaixo exibe a utilização do método Post ao invés do método Send:

using (HttpClient http = new HttpClient(“http://localhost:1572/ServicoDeUsuarios.svc/&#8221;))
{
    Usuario u = new Usuario() { Codigo = 123, Nome = “Israel” };

    using (HttpResponseMessage response =
        http.Post(“Adicionar”, HttpContentExtensions.CreateDataContract<Usuario>(u))
    {
        response.EnsureStatusIsSuccessful();
        Console.WriteLine(response.Content.ReadAsXElement().Value);
    }
}

Os métodos utilizados para efetivamente invocar uma operação, retorna uma instância da classe HttpResponseMessage, que como o próprio nome diz, representa o resultado da requisição. Entre os vários métodos que essa classe fornece, alguns métodos de extensão foram adicionados a ele, através da classe HttpMessageExtensions. Entre esses métodos, temos um chamado de EnsureStatusIsSuccessful, que verifica se a requisição foi realizada com sucesso, e se não foi, uma exceção será disparada. Esse método recorre, internamente, ao método público e estático EnsureStatusIs, que dado um código de resposta do protocolo HTTP, verifica se a resposta é igual a este código, disparando uma exceção caso não tenha sido.

E, assim como o objeto que representa a requisição, o objeto de resposta também possui uma propriedade chamada Content, que retorna a instância de uma classe do tipo HttpContent. Essa classe também recebe alguns métodos de extensão, que podemos ler o seu conteúdo já deserializando em um determinado tipo. Para isso, temos os seguintes métodos, que estão prefixados com a classe que define a extensão:

  • HttpContent.ReadAsByteArray: Retorna um array de bytes que representa o corpo da resposta.
  • HttpContent.ReadAsStream: Retorna um stream que contendo o corpo da resposta.
  • HttpContent.ReadAsString: Retorna uma string contendo o corpo da resposta.
  • XElementContentExtensions.ReadAsXElement: Retorna um objeto do tipo XElement, que pode ser utilizado para interagir com o corpo da mensagem retornado pelo serviço.
  • DataContractContentExtensions.ReadAsDataContract: Retorna um objeto (definido pelo parâmetro genérico) que corresponde ao corpo da mensagem.

Como sabemos que o resultado da operação é formatado em Xml, podemos optar por ler utilizando o método ReadAsXElement, que nos dará um objeto do tipo XElement, onde podemos recorrer à propriedade Value para visualizar a string que foi retornado pelo serviço.

Para finalizar, temos o segundo método, que é o RecuperarUsuarios, que através do modelo GET, retornará um array contendo os usuários. A única mudança considerável em relação ao código anterior, é que utilizamos o método Get da classe HttpClient, e para capturar o resultado, recorremos ao método ReadAsDataContract<T>, para já converter na coleção que criamos acima.

using (HttpClient http = new HttpClient(“http://localhost:1572/ServicoDeUsuarios.svc/&#8221;))
{
    using (HttpResponseMessage response = http.Get(“RecuperarUsuarios”))
    {
        response.EnsureStatusIsSuccessful();

        ColecaoDeUsuarios usuarios =
            response.Content.ReadAsDataContract<ColecaoDeUsuarios>();

        foreach (var item in usuarios)
        {
            Console.WriteLine(item.Nome);
        }
    }
}

Conclusão: Essa biblioteca auxilia muito no consumo de serviços que foram expostos através do modelo REST, independentemente se eles foram ou não criados através do WCF. Essa biblioteca facilitará o consumo destes tipos em serviços em qualquer aplicativo que seja construído em cima da plataforma .NET, sem a necessidade de conhecer as classes de baixo nível.

HttpClient.zip (142.60 kb)

REST e o WSDL

Há algum tempo, eu escrevi um artigo sobre como expor serviços construídos em WCF através do modelo REST, e além disso, foi comentado também sobre as principais diferenças no modelo REST com SOAP, apontando as características de cada uma deles. Um dos detalhes que ficou para trás foi o documento WSDL.

Como sabemos, o WSDL (Web Service Description Language), é um documento baseado em XML, que descreve todas as características (definições) de um determinado serviço, exposto através do modelo SOAP. É justamente esse documento que é utilizado por ferramentas como o svcutil.exe e a opção “Add Service Reference” do Visual Studio, para gerar uma classe que representará o proxy, que por sua vez, será utilizado pelos clientes para consumir o serviço como se fosse uma classe local, mas durante a execução, a mensagem será enviada ao ponto remoto.

Em serviços baseados no modelo REST, não temos essa opção, ou seja, pois o documento/padrão WSDL não suporta endpoints baseados em formato REST. Uma das dificuldades que isso causa é a não possibilidade de gerar um proxy do lado do cliente para facilitar o consumo deste serviço. Além disso, também iremos nos deparar com a dificuldade em visualizar quais são as operações, parâmetros e eventuais resultados que o serviço expõe para seus clientes.

A finalidade do artigo é abordar como resolveremos o segundo problema, ou seja, a exposição das informações pertinentes à documentação, ou melhor, à descrição do serviço. A primeira opção para expor isso, é utilizando o behavior WebHttpBehavior, que expõe uma propriedade chamada HelpEnabled, que quando definida como True, exibe uma página em formato HTML, com todas as operações que o serviço possui, e com exemplos de como invocar cada uma delas. Abaixo temos o código (declarativo) necessário para habilitar este recurso, e em seguida, temos a imagem que corresponde a página gerada automaticamente:

<?xml version=”1.0″?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=”Service”>
        <endpoint
          address=””
          binding=”webHttpBinding”
          contract=”IService”
          endpointConfiguration=”edpConfig” />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name=”edpConfig”>
          <webHttp helpEnabled=”true”/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

A outra possibilidade que temos é utilizando um novo recurso, que é disponibilizado a partir do WCF-REST Starter Kit, que é conhecido como Automatic Help Page. Tudo o que precisamos fazer aqui é a mudar a factory responsável por criar os hosts que gerenciam a execução do serviço. Depois de instalar o  WCF-REST Starter Kit, precisamos referenciar o assembly Microsoft.ServiceModel.Web.dll, que fornecerá uma classe chamada WebServiceHost2Factory, e devemos utilizá-la no arquivo *.svc, como é mostrado abaixo:

<%@ ServiceHost
    Language=”C#”
    Debug=”true”
    Service=”Service”
    Factory=”Microsoft.ServiceModel.Web.WebServiceHost2Factory”
    CodeBehind=”~/App_Code/Service.cs” %>

Com essa funcionalidade habilitada, podemos acessar a página que irá descrever o serviço, acrescentando à URL do serviço a string “/help”, como podemos notar na imagem abaixo. Note que passamos a ter a descrição do mensagem de requisição e resposta, e além disso, podemos acrescentar mensagens customizadas nas nossas operações, utilizando o atributo WebHelpAttribute, que automaticamente aparecerá nesta mesma página, fornecendo ainda mais informações para facilitar a vida de quem consumirá a respectiva operação.

Conclusão: Apesar dos recursos que vimos neste artigo, isso ainda não é capaz de facilitar a vida de quem o consome, já que ainda será necessário a criação das requisições e o tratamento das respostas de forma bastante manual. Há ainda alguns novos recursos, também fornecidos pelo WCF-REST Starter Kit, quais você pode combinar com essa documentação, e tornar o consumo mais simples.