WCF Web API Test Client

by Israel Aece 3. October 2011 22:21

Uma das ferramentas que a Microsoft criou para facilitar o teste de serviços WCF baseados em SOAP foi o WCF Test Client. Este utilitário permite consumir serviços simples através de alguns protocolos, sem a necessidade de ter que escrever uma aplicação cliente para apenas testá-lo.

Já para testar serviços baseandos em REST, esse utilitário não ajuda. Podemos recorrer ao navegador. Mas ele também não vai ajudar. Não é possível testar métodos POST diretamente, ele não consegue interpretar o formato JSON, não é possível customizar headers, etc. Por conta de todas essas "limitações", precisamos de uma ferramenta extra para nos auxiliar nos testes destes tipos de serviços. Isso nos leva a instalar uma aplicação de terceiros, como por exemplo, o Fiddler.

Apesar do Fiddler ajudar imensamente, ainda é um pouco complicado, pois precisamos descobrir quais são as URIs e seus respectivos métodos HTTP que foram disponibilizados pelo serviço. Para facilitar os testes, a Microsoft incorporou na WCF Web API uma ferramenta, escrita em jQuery, para que possamos testar os serviços tão logo quando eles forem criados, já listando os endereços disponíveis e com uma interface acessível no navegador que conduz facilmente os testes. Esta ferramenta é chamada de WCF Web API Test Client.

Por padrão, esse recurso está desabilitado. Para habilitá-lo precisamos customizar as configurações do serviço, utilizando a classe HttpConfiguration. Essa classe expõe uma propriedade boleana chamada EnableTestClient. O código abaixo ilustra como efetuar a configuração no arquivo Global.asax:

RouteTable.Routes.MapServiceRoute<ServicoDeUsuarios>
    ("usuarios", new HttpConfiguration() { EnableTestClient = true });

Ao definir esta propriedade como True, podemos acrescentar o sufixo "/test" na endereço do serviço, e a interface qual foi mencionada acima é exibida no navegador, e dali em diante, podemos utilizá-la para efetuar os testes. As imagens abaixo exibem alguns dos testes que foram capturados em um exemplo simples, que posta informações e também captura os dados que foram inseridos.

 

 

Tags: , ,

WCF

Recursos da Palestra do TechEd Brasil 2011

by Israel Aece 1. October 2011 10:40

Ontem eu fiz uma palestra no TechEd Brasil 2011, e o principal assunto abordado lá foi a nova API que a Microsoft está criando para facilitar a construção, hospedagem e consumo de serviços WCF baseados em REST.

O exemplo principal foi concetrado um projeto que nomeie de Techitter, que é composto por algumas aplicações simulando - de muito longe - o Twitter. A solução é composta por 4 projetos, onde um é o serviço, e onde concentra-se grande parte das novidades, um site em ASP.NET MVC que via jQuery posta e carrega as mensagens; outro projeto que consome a mesma API como um widget e, finalmente, uma aplicação WPF que também faz uso do serviço, consumindo o mesmo serviço via HttpClient, que também compõe a nova API. Para incrementar, existe o consumo de um serviço criado em outra tecnologia (NodeJs.exe), que foi consumido pelo serviço.

Gostaria de agradecer a todos os presentes, e também ao Rogerio Cordeiro pela oportunidade. Para aqueles interessados, o download do projeto pode ser baixado clicando aqui.

Tags: , ,

General | WCF

Caching em Serviços REST

by Israel Aece 28. August 2011 17:56

Uma das principais técnicas que são utilizadas na Web em busca de performance, é a utilização de técnicas de caching. Com elas, podemos armazenar em algum local uma cópia da página (HTML), arquivo, imagem, etc., que estamos acessando, para quando precisarmos novamente daquele recurso (URL) mais tarde, sermos atendidos de uma forma muito mais rápida.

A redução da latência e a diminuição do tráfego na rede são dois detalhes que alguma estratégia de caching pode ajudar a melhorar. O controle do caching pode estar definido do lado do servidor ou diretamente no cliente que consome o recurso. Sendo assim, ao acessar um recurso que já foi acessado recentemente, o usuário será servido quase que instantaneamente com o - mesmo - resultado.

As estratégias de caching são parte da especificação do protocolo HTTP, que como sabemos, foi desenhado para ser utilizado para acessar informações de sistemas distribuídos. A ideia principal do caching do HTTP, é tentar eliminar, quando possível, a necessidade de fazer novas requisições para um mesmo recurso, ou quando a requisição é necessária, tentar diminuir a quantidade de dados devolvidos.

Na verdade, dois detalhes importantes são definidos na especificação do protocolo, onde a estratégia de caching deve/pode controlar, a saber: expiração e validação. A expiração consiste em verificar se o recurso que está sendo solicitado já está em cache e continua sendo válido (ainda não expirado). Já a validação verifica se o recurso que está sendo re-solicitado ao servidor, sofreu ou não mudanças desde a última requisição, e caso não tenha, nada será retornado, afinal já temos a versão mais atualizada, e isso reduz a quantidade de informações - desnecessárias - sendo trafegadas.

Serviços REST, por estarem fortemente ligados ao protocolo HTTP, podem também fazer uso do caching oferecido pelo protocolo. A ideia deste artigo é demonstrar as opções que temos para configurar e, consequentemente, fazer uso de todos os benefícios oferecidos pelo caching. Para o exemplo, teremos um serviço que gerencia produtos, e haverá um único método que dado um identificador, retorna a instância do produto correspondente. Abaixo temos o código do serviço que utilizaremos pelos exemplos:

[ServiceContract]
[
    AspNetCompatibilityRequirements
    (
        RequirementsMode = AspNetCompatibilityRequirementsMode.Required
    )
]
public class Servico
{
    [WebGet(UriTemplate = "produtos/{produtoId}")]
    public Produto RecuperarProduto(string produtoId)
    {
        var produto =
            (
                from p in produtos
                where p.Id == Convert.ToInt32(produtoId)
                select p
            ).SingleOrDefault();

        return produto;
    }
}

Vou omitir a configuração do serviço aqui, mas neste primeiro momento estaremos utilizando a infraestrutura fornecida pelo WCF para a construção de serviços baseado em REST, que está disponível desde a versão 3.5 do .NET Framework (WCF Web Http). Ao subir este serviço, ele estará acessível e toda e qualquer requisição realizada pelo navegador chegará até o ele.

Todo o controle do caching do HTTP é realizado através de headers que foram criados e são assegurados pelo navegador e pelo servidor, para controlar o caching criado. Os serviços WCF fornecem alguns recursos que nos permitem acessar de forma mais simples estes headers, manipulando-os de acordo com a nossa necessidade. Os headers são divididos em cartegorias para manipular a expiração e a validação, e inicialmente vamos analisar aqueles que são utilizados para determinar a expiração de um recurso.

Expiração

O HTTP 1.1 introduziu um novo header, que é responsável por controlar a expiração. Este header é o Cache-Control, que pode receber vários valores que combinados, instruem o cliente a como efetuar e controlar o caching do recurso que está sendo devolvido. Um dos valores que podemos definir no Cache-Control é private, que determina que aquele conteúdo é exclusivo à um usuário (navegador), e pode eventualmente ser cacheado por ele. Podemos combiná-lo com outro valor que é o max-age, que define um número inteiro, que determina quantos segundos depois da requisição o conteúdo deverá ser mantido no cache.

Sendo assim, podemos recorrer ao contexto da requisição atual (WebOperationContext) dentro do método que está sendo disparado, e lá incluir o header que mencionamos acima. Temos que acessar a coleção de headers a partir da propriedade OutgoingResponse, que permite acessar as informações pertinentes à resposta.

WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "private, max-age=30");

Já quando não quiser que o cliente efetue o cache do recurso, podemos recorrer ao mesmo código acima para explicitamente determinar isso, determinando o valor do header Cache-Control como no-cache. O código abaixo exibe esta configuração:

WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");

Note que para configurarmos o header Cache-Control, utilizamos uma classe pertinente ao WCF, que independe do host que estamos utilizando para hospedar o serviço. Quando utilizamos o IIS e, consequentemente, a infraestrutura do ASP.NET, podemos fazer uso de um recurso que já existe há algum tempo, que é o OutputCache.

A configuração do caching é através de cache profiles, que é um recurso onde você define todas as características do caching no arquivo de configuração (Web.config), e vincula essa configuração através do atributo AspNetCacheProfileAttribute. Abaixo temos o arquivo de configuração do serviço, e podemos notar a configuração do caching. A primeira configuração importante é habilitar o modelo de compatibilidade com o ASP.NET, e fazemos isso através do atributo aspNetCompatibilityEnabled do elemento serviceHostingEnvironment, definindo-o como True.

Em seguida, habilitamos o OutputCache definindo o atributo enableOutputCache como True. Logo após, temos um profile criado, que agrupa o conjunto de configurações de cache e o nomeia. Repare que estamos definindo o local do cache como "Server", o que determina que uma vez atendida a requisição para um determinado recurso, o cache será armazenado no servidor, e qualquer requisição subsequente, partindo ou não do mesmo cliente, será devolvida do cache criado, respeitando a duração de 60 segundos, ali também configurada. Isso fará com que qualquer complexidade que temos na execução daquele recurso, o preço será pago apenas uma única vez.

<configuration>
  <system.web>
    <caching>
      <outputCache enableOutputCache="true" />
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="CachingNoServidor"
               location="Server"
               duration="60"
               varyByParam="none"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>
  </system.web>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <services>
      <service name="ViaWcfWebHttp.Servico">
        <endpoint address=""
                  binding="webHttpBinding"
                  contract="ViaWcfWebHttp.Servico"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Como disse anteriormente, a amarração da configuração com o método do serviço é realizada através do atributo AspNetCacheProfileAttribute, que recebe em seu construtor uma string que representa o nome do profile que você quer aplicar para ele. Abaixo temos o serviço que foi ligeiramente modificado, decorado com o respectivo atributo:

[AspNetCacheProfile("CachingNoServidor")]
[WebGet(UriTemplate = "produtos/{produtoId}")]
public Produto RecuperarProduto(string produtoId) { }

Como esse caching refere-se ao servidor, os clientes nada sabem sobre ele. Inclusive na resposta ao cliente, por padrão, ele define o header Cache-Control como no-cache. Como já era de se esperar, podemos criar um novo profile, e lá definirmos o local do cache no cliente. Isso fará com que ao acessar o serviço, o cliente receberá o header Cache-Control definido como private e o atributo max-age definido como 60 (segundos), fazendo com que o navegador faça o caching do recurso. Abaixo temos o trecho do arquivo de configuração que exibe a adição deste profile de caching para o cliente:

<outputCacheSettings>
   <outputCacheProfiles>
      <add name="CachingNoCliente"
           location="Client"
           duration="60"
           varyByParam="none"/>
   </outputCacheProfiles>
</outputCacheSettings>

Validação

Acima vimos as opções que temos para controlarmos a expiração. Notamos que a expiração controla o período em que o recurso ficará disponível no caching do cliente (ou do servidor). Isso faz com que alguns recursos não sejam requisitados a todo momento, diminuindo a quantidade de informações que trafegam entre as partes. Só que dependendo do conteúdo que está sendo armazenado no cache, nem sempre podemos confiar cegamente no conteúdo que está salvo do lado do cliente.

Em certos cenários, o custo do round-trip é ignorado, em busca dos dados mais recentes possíveis, onde o prejuízo de visualizar uma informação defasada é muito maior. Só que as vezes, vamos até o servidor em busca dessa nova informação, e ela ainda não foi alterada, retornando para o cliente o mesmo conteúdo, atualizando a informação que temos localmente com exatamente, a mesma informação, ou seja, trafegamos uma informação que já tínhamos do lado do cliente. Para otimizar isso, temos uma técnica que chamamos de "GET condicional".

Essa técnica faz uso de entity tags (ETags), que consiste na adição de um header que é adicionado na resposta à uma requisição, que caracteriza a "versão" do conteúdo que foi devolvido ao cliente. Ao receber esse conteúdo e encaminhar esta tag nas requisições subsequentes, o serviço deverá ser capaz de identificar se houve ou não mudanças no objeto; caso exista, o serviço irá retornar a versão mais recente, do contrário, retornará o status 304 do HTTP, que indica que o recurso não teve mudanças, sem qualquer conteúdo na resposta, poupando assim que informações desnecessárias sejam enviadas ao cliente, que por sua vez, já possue a versão mais atual.

A implementação consiste em duas partes, sendo a primeira onde devemos nos preocupar em passar ao cliente as informações que caracterizam a "versão" do conteúdo, e a segunda parte, onde devemos criar dentro do serviço, a lógica para determinar se a versão que o cliente possui já é ou não a mais recente.

Para nosso exemplo, vamos incrementar a classe Produto com uma propriedade do tipo Guid chamada Versao, que define a versão do mesmo. Qualquer alteração que ocorra em qualquer uma das propriedades do produto, deverá atualizar o GUID. Já que essa propriedade irá caracterizar o versionamento, devemos apontar na resposta para o cliente o seu valor para que ele possa encaminhar ao mesmo através do header ETag. E para isso, vamos novamente recorrer a classe WebOperationContext, acessando a resposta que será enviada ao cliente e, finalmente, teremos o método SetETag, que como o próprio nome diz, permite definirmos a tag:

[WebGet(UriTemplate = "produtos/{produtoId}")]
public Produto RecuperarProduto(string produtoId)
{
    var produto =
        (
            from p in produtos
            where p.Id == Convert.ToInt32(produtoId)
            select p
        ).SingleOrDefault();

    WebOperationContext.Current.OutgoingResponse.SetETag(produto.Versao);
    return produto;
}

Ao utilizar uma ferramenta para monitorar o tráfego HTTP, podemos visualizar o resultado da requisição para o serviço acima. Note a presença do header ETag definido com o GUID gerado para nosso objeto:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 28 Aug 2011 20:26:53 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 202
ETag: "8e548670-8b75-4187-aedc-8182f46c3216"
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close

<Produto xmlns="http://schemas.datacontract.org/2004/07/ViaWcfWebHttp" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Descricao>Mouse Microsoft</Descricao><Id>1</Id><Valor>120.00</Valor></Produto>

Uma vez que o cliente recepciona essa requisição, as requisições subsequentes repassam o valor do header ETag para o serviço através de um outro header, chamado de If-None-Match, onde o serviço deverá averiguar se o produto foi alterado nesta janela de tempo, para determinar se o conteúdo do cliente deve ou não ser atualizado. Abaixo temos a requisição sendo realizada ao serviço depois de receber a ETag:

GET http://localhost:3911/Servico.svc/produtos/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,pt-BR;q=0.5
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost:3911
If-None-Match: "8e548670-8b75-4187-aedc-8182f46c3216"
Connection: Keep-Alive

Para efetuar essa validação do lado do serviço, há um método chamado CheckConditionalRetrieve, que está disponível a partir da propriedade IncomingRequest da classe WebOperationContext. Esse método recebe como parâmetro a ETag do produto. Se o produto não foi modificado, então a tag da requisição será a mesma, o que faz com que o método, internamente, aborte a requisição, devolvendo ao cliente o resultado 304 do HTTP, que caracteriza que nada foi mudado (NotModified). O código a seguir exibe a utilização deste método, e logo na sequência, temos a imagem que ilustra esse procedimento:

[WebGet(UriTemplate = "produtos/{produtoId}")]
public Produto RecuperarProduto(string produtoId)
{
    var produto =
        (
            from p int produtos
            where p.Id == Convert.ToInt32(produtoId)
            select p
        ).SingleOrDefault();

    WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(produto.Versao);
    WebOperationContext.Current.OutgoingResponse.SetETag(produto.Versao);

    return produto;
}

WCF Web API

Como já comentei aqui, a Microsoft trabalha em um novo projeto chamado WCF Web API, que torna o desenvolvimento e consumo de serviços REST mais simples. Estes tipos de serviços também podem tirar proveito dos recursos de caching fornecidos pelo HTTP que vimos neste artigo.

Ao utilizar os objetos que representam as mensagens de requisição e resposta diretamente, você encontrá propriedades que configuram o caching no cliente. Você também pode optar por utilizar um message handler, para separar o que corresponde ao negócio do que compete a parte de infraestrutura.

Tags: , ,

WCF

Coletando e analisando arquivos de Dump

by Israel Aece 13. August 2011 15:37

Ao instalar uma aplicação em seu local de funcionamento, começa uma nova etapa, que é o monitoramento da mesma, que permite diagnosticar eventuais falhas e tentar trabalhar sempre de uma forma preventiva. Só que muitas vezes, estes problemas podem acontecer durante a execução, problemas estes que podem ser por falta de testes que atendiam aquelas condições, ou ainda, referente à algum problema de infraestrutura.

Se você toma o devido cuidado de catalogar as exceções que estão sendo disparadas, você pode recorrer aos arquivos onde elas estão armazenadas e inspecionar o que aconteceu e, consequentemente, encontrar a região do código responsável por aquele problema, e resolvê-lo, para em seguida, distribuir a correção para ele. Só que para detectar o problema, geralmente precisamos muito mais do que simplesmente o tipo da exceção que foi disparada, mas também quais foram os parâmetros informados, os valores que as variáveis estavam definidas naquele momento, e assim por diante.

Quando estamos em ambiente de desenvolvimento, podemos recorrer ao debugger do Visual Studio para executar a aplicação passo à passo, parando nos pontos que são importantes e naqueles que podem estar ocasionando o problema, e atenciosamente analisar e detectar o problema. Só que se essa aplicação já estiver em funcionamento, isso não é possível, pelo menos não de uma forma simples. Neste caso podemos recorrer a criação de um arquivo de dump da aplicação em questão. Ao gerar este arquivo, ele "fotografa" o estado atual do processo e gera várias informações para que possamos depurá-la mais tarde, algo mais ou menos parecido com o reporte de bugs do Windows.

Esse recurso é algo que podemos utilizar tanto em aplicações cliente como aquelas que rodam em um servidor, como é o caso de aplicações ASP.NET ou de serviços WCF. No caso de aplicações cliente, você pode recorrer ao Doctor Watson do Windows ou indo diretamente através da Task Manager. Como eu quero demonstrar a coleta do dump de um serviço WCF, vamos recorrer à segunda opção. Como sabemos, ao hospedar um serviço WCF no IIS, quem será responsável por executá-lo é um processo, chamado de w3wp.exe, que pode ser configurado através da opção Application Pools. Abaixo temos a imagem que ilustra como extrair o dump do processo do IIS:

Basicamente, este serviço recebe uma requisição e gera uma massa de informações para o respectivo cliente. Depois do arquivo de dump criado (extensão *.dmp), você receberá uma notificação informando onde ele foi salvo. Tudo o que precisamos fazer agora, é abrir o arquivo no Visual Studio 2010. A partir desta versão, o Visual Studio é capaz de interpretar esses arquivos. Ao abrí-lo, você irá se deparar com uma página, que detalha as informações sobre o processo. Podemos visualizar o nome do processo de onde o dump foi extraído, versão do sistema operacional, da CLR, etc. Outro ponto importante são os módulos que foram carregados à este processo, que aqui, por questões de espaço, não estão sendo exibidos todos eles. A imagem abaixo ilustra este resumo:

Como este arquivo contém o estado da aplicação naquele momento, o Visual Studio nos permite analisar o estado das informações (variáveis, parâmetros, etc.) que tínhamos, de uma forma tão simples quanto depurar a aplicação. Só que para isso funcionar, é necessário definirmos os símbolos, que são responsáveis por armazenarem informações para guiar o debugger até o código fonte. A opção "Set symbol paths" nos permite definir o endereço até o arquivo *.pdb, gerado durante o desenvolvimento do serviço.

Depois disso definido, podemos clicar na opção "Debug with Mixed", e o debugger do Visual Studio entra em ação. Ele lerá as informações contidas no arquivo de dump e irá exibí-las através das ferramentas de depuração que o próprio Visual Studio já nos fornece. A imagem que veremos a seguir já é uma dessas ferramentas: a janela Parallel Stacks. Essa janela é útil para depurarmos aplicações multi-thread, como é o caso de serviços WCF. Como podemos visualizar, temos a stack da execução e dentro dela, a chamada para o método Executar, que faz parte do serviço criado para o teste.

Ao clicar com o botão direito em um dos itens, podemos alternar entre as threads que estavam em execução naquele momento, acessando a operação Executar. Note que entre parênteses há o valor do parâmetro que está sendo passado para a operação. Abaixo temos a imagem que ilustra as três threads que temos em execução:

Finalmente, ao selecionar uma das opções listadas na imagem acima, somos encaminhados para o código correspondente, conseguindo visualizar o estado das informações, simplesmente passando o mouse por cima das mesmas. Além disso, você ainda pode recorrer as janelas de depuração que já existem há algum tempo no Visual Studio, como é caso das janelas de Threads, Modules e Locals, que trazem informações pertinentes aquele código que está sendo depurado no momento. A imagem abaixo exibe o estado das variáveis e parâmetros da operação Executar no momento em que o arquivo de dump foi gerado:

Tags:

WCF

Utilizando o utilitário netstat

by Israel Aece 10. August 2011 23:29

Quando hospedamos mais que um serviço em uma mesma máquina, pode ser difícil gerenciar, e de uma forma simples, visualizar o que está ativo e aceitando requisições. Se não temos o luxo de utilizar algum sistema de gerenciamento mais eficiente, como é o caso do AppFabric, podemos recorrer à um utilitário de linha de comando que existe no sistema operacional chamado netstat.

Este popular utilitário exibe uma lista com as conexões de rede estabelecidas no computador onde o mesmo está sendo executado. Se tivermos serviços WCF hospedados em uma determinada máquina, é possível visualizá-las através deste utilitário, e no mínimo, enxergar em qual porta eles encontram, se ela (porta) está aberta ou não e seu status. Abaixo temos um código de teste, que cria e abre vários hosts dentro de uma aplicação Console.

static void Main(string[] args)
{
    ServiceHost[] hosts = new ServiceHost[10];

    for (int i = 0; i < 10; i++)
    {
        var host = 
            new ServiceHost(typeof(Servico), 
                new Uri(string.Format("http://localhost:987{0}", i)));

        host.AddServiceEndpoint(typeof(IContrato), new BasicHttpBinding(), "srv");
        host.Open();
        hosts[i] = host;
    }

    Console.ReadLine();
}

Ao rodar esta aplicação, dez hosts são criados e abertos. Ao executar o utilitário netstat, essas conexões já são listadas, assim como podemos perceber na imagem abaixo:

Tags: ,

WCF

Expondo Tipos POCO através do WCF

by Israel Aece 4. July 2011 23:02

Ao construir serviços WCF, podemos expor tipos que vão além dos tipos tradicionais (tais como strings, inteiros, etc.), ou seja, podemos recorrer a construção de tipos customizados, onde construimos classes com suas propriedades que determinam os dados que serão carregados entre o cliente e o serviço.

Algumas vezes essas classes são construídas com o intuito de atender a requisição e/ou resposta das operações de um determinado serviço, mas que internamente, são consultas que fazemos no banco de dados e estamos retornando ao cliente, ou ainda, são comandos que devemos executar, também no banco de dados, para persitir alguma informação.

Se optarmos pelo uso do Entity Framework, podemos utilizar o modelo de geração POCO, onde as classes geradas são consideradas "puras", ou seja, não dependem de nenhuma classe do framework de persistência. Isso tornam as classes simples, apenas com as propriedades que são, em princípio, mapeadas para colunas da base de dados. Sendo assim, muitos podem optar pela exposição direta destas classes no contrato de um serviço WCF, evitando assim a criação de outras classes (DTOs) para efetuar o transporte das informações.

Mas expor diretamente essas classes podem acarretar alguns problemas. A questão é que entidades que foram geradas utilizando o modelo POCO, possuem um comportamento durante a execução diferente das entidades que são geradas pelo modelo padrão do Entity Framework. Quando utilizamos o modelo padrão, as entidades herdam diretamente da classe EntityObject, que fornece toda a infraestrutura necessária para efetuar o rastreamento das mudanças e dá também o suporte ao lazy-loading. Quando utilizamos classes POCO, esses recursos continuam sendo oferecidos, mas são implementados de forma diferente.

Neste caso, o Entity Framework gera proxies dinâmicos para interceptar os acessos às propriedades das entidades que estão sendo manipuladas, e com isso, todos os recursos acima, continuam ainda sendo suportados. Abaixo temos o objeto Type da classe que foi gerada dinamicamente.

{System.Data.Entity.DynamicProxies.Cliente_B38C1C71074F1A0CDCF11548021F55AF4BAC2116057847A45ACD8E600D02BB90}

Só que ao expor esse tipo de objeto diretamente através do WCF, vamos nos deparar com uma exceção durante a execução. Isso se deve ao fato de que o WCF não é capaz serializar/deseralizar tipos que não são conhecidos pelo mesmo. Ao construir o contrato para um serviço WCF, você pode mencionar os tipos das classes geradas efetivamente pelo EDMX, mas durante a execução, o tipo acima será criado e, consequentemente, será entregue para a infraestrutura do WCF, ele irá se certificar de que o tipo é conhecido no contrato, e como não é, a exceção será disparada.

Há algumas alternativas para evitar esse problema. A primeira delas é desabilitar a criação do proxy dinâmico através da propriedade boleana ProxyCreationEnabled, que está acessível através do contexto do Entity Framework, assim como é exibido no código abaixo. O problema é que ao fazer isso, entidades criadas no padrão POCO não serão capazes de fornecer as funcionalidades de rastreamento de mudanças e lazy-loading.

public class Servico : IContrato
{
    public Cliente Recuperar(int id)
    {
        using (DBTestesEntities1 ctx = new DBTestesEntities1())
        {
            ctx.ContextOptions.ProxyCreationEnabled = false;

            return (from c in ctx.Cliente where c.ClienteId == id select c).Single();
        }
    }
}

A outra opção é a criação de um behavior que pode ser aplicado em nível de operação, alterando o serializador padrão do WCF, que é o DataContractSerializer, para o serializador ProxyDataContractResolver. Este serializador, que está debaixo do namespace System.Data.Objects, ajuda na resolução dos tipos que são criados dinamicamente pelo Entity Framework em "tipos concretos", recorrendo ao método estático GetObjectType, exposto através da classe ObjectContext, que dado o Type do proxy dinâmico, ele retorna o tipo correspondente POCO, informando ao WCF que Type correto para que ele possa ser capaz de serializá-lo. Abaixo temos um exemplo da implementação e aplicação deste behavior:

public class ProxyDataResolverAttribute : Attribute, IOperationBehavior
{
    public void ApplyDispatchBehavior(OperationDescription opDescription, DispatchOperation dop)
    {
        opDescription
            .Behaviors
            .Find<DataContractSerializerOperationBehavior>()
            .DataContractResolver = new ProxyDataContractResolver();
    }

    //outros métodos
}

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    [ProxyDataResolver]
    Cliente Recuperar(int id);
}

E, finalmente, uma terceira opção, seria a utilização de DTOs (Data Transfer Objects), onde você pode definir os objetos que quer trafegar entre as partes, sem a necessidade de se preocupar com detalhes de uma tecnologia específica que esteja utilizando nos bastidores e, principalmente, de ter a possibilidade de desacoplar complementamente o que representa os seus dados/domínio daquilo que quer expor para os clientes/parceiros.

Tags: , , ,

Data | WCF

WCF Web API - API Key Verification

by Israel Aece 31. May 2011 22:25

Quando criamos algum tipo de serviço, em princípio nos concentramos nas funcionalidades e informações que ele irá expor aos consumidores. Depois disso, nos preocuparemos em analisar o contexto de segurança, verificando o tipo de autenticação e nível de autorização que são necessários para o serviço funcionar de forma segura.

O detalhe é que muitas vezes não queremos envolver qualquer mecanismo de autenticação e autorização no serviço, já que ele poderá ser acessado de forma pública, sem qualquer necessidade do consumidor efetivamente se cadastrar, gerar um login e senha e, finalmente, acessar o respectivo serviço, apresentando à ele essas informações. Mas apesar do cliente não se identificar com um login e senha, não quer dizer que nosso serviço não precise ser "protegido" de alguma outra forma.

A questão é que permitindo o acesso irrestrito total, permite com que bons usuários o acessem da forma legal, e também permite com que outros usuários, estes mal intencionados, disparem uma infinidade de requisições contra o serviço, e se este, por sua vez, pode estar consumindo recursos caros, com por exemplo, acessando uma base de dados, em pouco tempo nosso servidor estará esgotado, não conseguindo atender nem mesmo aqueles que usam o serviço da forma correta.

Uma técnica chamada API Key Verification foi criada com o intuito de evitar esse tipo de problema. Com uma implementação simples, criamos e mantemos uma lista de chaves (podendo ser strings, inteiros, Guids, etc.) que estão habilitadas à acessarem o serviço. Os consumidores podem receber esta chave de alguma forma (out-of-band ou não), como por exemplo, fazendo um cadastro no site da empresa demonstrando o interesse; mais tarde, ele receberá um e-mail contendo a chave que foi criada exclusivamente para o mesmo, e que deverá ser utilizada quando efetuar uma requisição para o serviço, embutindo-a na coleção de headers ou querystrings, de acordo com o que você determinar. Ao chegar a requisição no serviço, você detecta a presença desta informação, e se a chave não fizer parte da requisição ou for uma chave inválida, deveremos rejeitar a mesma.

A ideia deste artigo é mostrar como podemos implementar esta validação utilizando a nova API do WCF, chamada WCF Web API. Para isso, vamos recorrer à um recurso exposto por ela, chamado de Message Handlers. Esses handlers são acoplados ao runtime, e são executados durante os primeiros estágios da requisição, antes mesmo do serviço ser efetivamente executado. Para maiores detalhes sobre os Message Handlers, consulte este artigo. Abaixo temos a implementação deste handler que validará a chave que o consumidor está nos encaminhando:

public class ApiKeyVerificationMessageHandler : DelegatingChannel
{
    private readonly IRepositorioDeChaves repositorioDeChaves;
    private const string API_KEY = "apiKey";

    public ApiKeyVerificationMessageHandler(HttpMessageChannel inner, IRepositorioDeChaves repositorio)
        : base(inner)
    {
        this.repositorioDeChaves = repositorio;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellation)
    {
        var queryStrings = HttpUtility.ParseQueryString(request.RequestUri.Query);
        var chave = Guid.Empty;

        if (Guid.TryParse(queryStrings[API_KEY], out chave) && repositorioDeChaves.VerificarSeExiste(chave))
            return base.SendAsync(request, cancellation);

        return Task.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.Unauthorized, "A chave fornecida é inválida."));
    }
}

Analisando a classe acima, podemos perceber que no construtor recebemos a instância de alguma classe que implemente a interface IRepositorioDeChaves. Utilizamos a interface aqui, justamente para tornar o nosso handler independente do local onde desejamos armazenar as chaves. Para efeito de testes, optei por criar uma implementação em memória e que ocultarei aqui por questões de espaço, mas você poderá recorrer a qualquer outro meio, como XML, SQL, etc. Em seguida, sobrescrevemos o método SendAsync (exposto pela classe DelegatingChannel), onde detectamos se existe ou não uma query string chamada apiKey, e se houver, fazemos o parser da mesma através do Guid, que é o tipo de dado que estamos utilizando para determinar o tipo das chaves que dão acesso ao serviço. Não existindo a chave na query string ou ela sendo inválida, retornamos uma mensagem informando o status como 401, que indica que a requisição não foi autorizada à acessar o serviço.

Depois disso, precisamos acoplar esse handler à execução, e para isso, utilizamos o evento ApplicationStart do arquivo Global.asax, onde vamos criar a factory responsável pela criação do handler. Essa factory será responsável por instanciar a classe ApiKeyVerificationMessageHandler que criamos acima, passando o repositório de chaves em memória que também criamos e configuramos dentro deste evento. Note que estou utilizando uma sobrecarga do método SetMessageHandlerFactory, que me permite passar um delegate contendo o código de construção do objeto, mas se quiser algo mais rebuscado, poderia construir uma factory herdando da classe HttpMessageHandlerFactory.

protected void Application_Start(object sender, EventArgs e)
{
    RepositorioDeGuidsEmMemoria repositorio = new RepositorioDeGuidsEmMemoria();
    repositorio.Adicionar(new Guid("d4907f3c-52c7-457e-9c18-e5abf9be77fe"));
    repositorio.Adicionar(new Guid("adc5c096-fdc3-4c57-a887-ba4a368f299f"));

    var config =
        HttpHostConfiguration
            .Create()
            .SetMessageHandlerFactory(inner => new ApiKeyVerificationMessageHandler(inner, repositorio));

    RouteTable.Routes.MapServiceRoute<ServicoDeTeste>("teste", config);
}

Depois dessas customizações, podemos utilizar o Fiddler para efetuar e capturar as requisições para o serviço. Para exemplificar, a primeira requisição informa um elemento chamado apiKey na coleção de query strings. Isso fará com que o serviço entregue a resposta da forma correta. Já na segunda requisição, omitimos a chave, fazendo com que o a resposta seja definida como 401, como também podemos perceber abaixo:

-- Primeira Requisição
GET http://localhost:1830/teste/ping?apiKey=d4907f3c-52c7-457e-9c18-e5abf9be77fe&value=sss HTTP/1.1
User-Agent: Fiddler
Host: localhost:1830

-- Resposta
HTTP/1.1 200 OK
Content-Length: 63
Content-Type: application/xml; charset=utf-8

<?xml version="1.0" encoding="utf-8"?><string>sss ping</string>

-- Segunda Requisição
GET http://localhost:1830/teste/ping?value=sss HTTP/1.1
User-Agent: Fiddler
Host: localhost:1830

-- Resposta
HTTP/1.1 401 Unauthorized
Content-Length: 0
Connection: Close

Além do que foi apresentado aqui, você ainda poderia criar políticas para renovação e revogação de chaves, e além disso, permitir com que o consumidor mande a chave na coleção de query strings ou de headers, podendo o handler ser inteligente o bastante para ser capaz de detectar em ambos os locais, e caso não encontrado, rejeitaria do mesmo jeito.

Tags: , , ,

WCF

WCF Web API - Formulários

by Israel Aece 10. May 2011 22:45

Sabemos que temos vários métodos suportados pelo protocolo HTTP, e entre eles, os mais comuns que vemos e utilizamos é o GET e o POST. Na primeira opção, o que enviamos ao servidor durante a requisição é o cabeçalho contendo tudo o que é necessário para efetuá-la, sem qualquer informação extra no corpo da mensagem. Ao contrário do que ocorre com o verbo POST, que além das informações que vão no cabeçalho, podemos também enviar qualquer conteúdo no corpo da mensagem.

Obviamente que o entendimento deste conteúdo por parte do serviço está condicionado ao header chamado Content-Type. Entre os vários tipos de conteúdo, temos os formatos Xml e Json. Mas ainda existe um outro formato, que também é muito comum em aplicações Web, que é conhecido como application/x-www-form-urlencoded. Este é o formato padrão que é utilizado pelo navegador, quando ele efetua um POST de um formulário HTML, codificando os campos existentes no mesmo, em uma coleção de pares de chave e valor.

Abaixo temos uma página HTML de exemplo, que contém um formulário simples, com dois campos do tipo Text e um botão que submete o formulário para o serviço que vamos criar mais tarde. Note que os controles estão envolvidos por um elemento chamado form, que contém o endereço (action) para onde o formulário será postado.

<form action="http://localhost:1989/paginas/Enviar" method="post">
  <input type="text=" id="Nome" name="Nome" />
  <input type="text=" id="Cpf" name="Cpf" />
  <input type="submit" name="Enviar" value="Enviar" />
</form>

Ao preencher os campos e postar o formulário, podemos capturar a requisição e analisar o que está sendo enviado ao serviço mencionado. Podemos notar a estrutura da requisição abaixo, e que alguns headers menos relevantes para a situação, foram omitidos por questões de espaço. A nossa análise começa pelo header, onde temos o Content-Type definido como application/x-www-form-urlencoded, que como falamos acima, corresponde ao valor padrão para formulários HTML. No corpo da mensagem temos os campos do formulário separados pelo caracter &. Já os espaços são substituídos pelo caracter +. E, para finalizar, o nome do controle é definido como chave, enquanto o conteúdo do controle é definido como valor na coleção.

POST http://localhost:1989/paginas/Enviar HTTP/1.1
Accept-Language: en-US,pt-BR;q=0.5
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
Content-Length: 50
Host: localhost:1989

Nome=Israel+Aece&Cpf=111.222.333-44&Enviar=Enviar

O WCF Web API possibilita a leitura deste tipo de requisição, e para isso, temos um media type específico, chamado de FormUrlEncodedMediaTypeFormatter. Em um artigo anterior eu comentei sobre os media types, mais deixei este media type de fora justamente para abordá-lo aqui. Assim como os media types Xml e Json, este também é adicionado por padrão à execução, mapeando o tipo application/x-www-form-urlencoded como sendo o formato que ele interpreta.

Quando você requisita alguma informação para um serviço construído sobre essa nova API, a escolha do media type é realizada no interior da classe ObjectContent, através de um método protegido chamado SelectReadFormatter. Esse método percorre os formatadores que temos adicionados, verificando se o tipo de conteúdo da requisição é suportado por ele, e sendo, ele é imediatamente retornado para que o próprio ObjectContent possa ler o conteúdo utilizando-o. Com o formatador em mãos, a classe ObjectContent invoca o método ReadFromStream, exposto pela classe MediaTypeFormatter, repassando a ele o stream contendo o corpo da requisição, que fará o parser da mesma, e devolvendo para o serviço o objeto já deserializado. Abaixo temos um método simples, chamado Enviar, que recebe a requisição do formulário HTML que fizemos acima:

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudo = request.Content.ReadAsString();

    return new HttpResponseMessage() { Content = new StringContent(conteudo) };
}

O grande problema do código acima, é que estamos lendo o corpo da mensagem como uma string, ficando difícil manipular e extrair informações do conteúdo enviado pelo cliente. Há algumas alternativas para conseguirmos extrair informações de uma forma mais amigável. A primeira delas é utilizando o método estático chamado ParseQueryString, da classe HttpUtility. Esse método recebe uma string e faz o parser dela, retornando o resultado (chaves e valores), em uma coleção especializada do tipo NameValueCollection. É importante lembrar que esta coleção é uma espécie de dicionário, suportando apenas strings como chave e como valor, e sua principal característica é não permitir chaves duplicadas, assim como qualquer dicionário, mas ela consegue agrupar valores que tenham a mesma chave, dentro de em um mesmo item. O código abaixo ilustra essa nova versão:

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudo = HttpUtility.ParseQueryString(request.Content.ReadAsString());
    var resultado = new StringBuilder();

    foreach (var item in conteudo.AllKeys)
        resultado.AppendLine(string.Format("{0}: {1}", item, conteudo[item]));

    return new HttpResponseMessage()
    {
        Content = new StringContent(resultado.ToString())
    };
}

A outra opção que temos, é utilizando classes que correspondem ao formato Json. Há um método estático chamado ParseFormUrlEncoded, fornecido pela classe FormUrlEncodedExtensions, que dado uma string representando o corpo codificado no formato que estamos vendo neste artigo, ele retorna um objeto do tipo JsonObject, que nada mais é do que uma espécie de dicionário, onde a chave é do tipo string e o valor do tipo JsonValue. Estes objetos também fazem parte desta nova API, quais já discuti neste outro artigo com maiores detalhes.

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudoCodificado = request.Content.ReadAsString();
    var conteudo = FormUrlEncodedExtensions.ParseFormUrlEncoded(conteudoCodificado);

    HttpResponseMessage response = new HttpResponseMessage();
    response.Content = new ObjectContent<JsonValue>(conteudo);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    return response;
}

Depois de extrair a string contendo o corpo da requisição, submetemos a mesma para o método ParseFormUrlEncoded, que retornará o objeto JsonObject com todo o conteúdo já no formato Json. Depois disso, definimos o objeto construído como corpo da mensagem de resposta e, finalmente, definimos o tipo da resposta como sendo do tipo application/json. Abaixo temos a resposta sendo devolvida para o cliente, retornando conforme o esperado:

HTTP/1.1 200 OK
Content-Length: 63
Content-Type: application/json

{"Nome":"Israel Aece","Cpf":"111.222.333-44","Enviar":"Enviar"}

Como vimos acima, o formato em que o formulário é encaminhado para o serviço, pode ser convertido implicitamente para Json, dando assim, um maior poder na manipulação da requisição e na geração dos resultados. Isso quer dizer que podemos também receber um conteúdo em formato application/x-www-form-urlencoded defindo no parâmetro do método, o tipo JsonObject ou invés de lidar diretamente com a classe HttpRequestMessage. Abaixo temos o método Enviar refletindo essa alteração:

[WebInvoke]
public HttpResponseMessage Enviar(JsonValue conteudo)
{
    HttpResponseMessage response = new HttpResponseMessage();
    response.Content = new ObjectContent<JsonValue>(conteudo);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    return response;
}

Finalmente, podemos utilizar o método AsDinamyc da classe JsonValue, que converte o nosso objeto em um tipo dinâmico, que permite burlar a verificação estática dos membros, fazendo isso somente em tempo de execução. Abaixo temos esse exemplo, e logo na sequência, o resultado gerado por este método e que foi capturado. Note que o código fica muito mais simples e expressivo.

[WebInvoke]
public HttpResponseMessage Enviar(JsonValue conteudo)
{
    dynamic valores = conteudo.AsDynamic();

    return new HttpResponseMessage()
    {
        Content = 
            new StringContent(string.Format("Nome: {0} - C.P.F.: {1}", valores.Nome, valores.Cpf))
    };
}

HTTP/1.1 200 OK
Content-Length: 46
Content-Type: text/plain; charset=iso-8859-1

Nome: "Israel Aece" - C.P.F.: "111.222.333-44"

Tags: , , ,

WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host