WCF Web API – Overview


Que o Windows Communication Foundation (WCF) é o pilar de comunicação dentro da plataforma .NET já é sabido. Com uma estrutura flexível, permite a construção de qualquer serviço independentemente de protocolo, pois ele não mantém ou menciona qualquer característica referente a isso, e apenas com a adição de uma pequena configuração (endpoint), é possível expor aquele serviço atendendo os mais diversos clientes.

Na versão 3.0 do .NET Framework, a Microsoft lançou o WCF, dando suporte a exposição de serviços aos mais diversos tipos de protocolos, “abraçando” várias tecnologias, onde as mensagens sempre são trocadas em formato SOAP. Muitas aplicações que precisam expor ou consumir serviços, as vezes não necessitam ter toda a complexidade e overhead que o SOAP impõe. Devido a isso, a Microsoft incluiu na versão 3.5 do .NET Framework, um conjunto de funcionalidades que permitem expor serviços construídos em WCF, seguindo os princípios REST, que possui uma afinidade com o protocolo HTTP, recorrendo a toda estrutura deste protocolo para manipular os serviços.

Mais tarde, a Microsoft também criou o WCF-Rest Starter Kit, que é uma biblioteca que traz várias funcionalidades à serviços REST, e facilita 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, tais como HttpWebRequest e HttpWebResponse, que estão dentro do namespace System.Net.

Depois de tudo isso, a Microsoft agora trabalha em um novo projeto chamado WCF Web API. Esta biblioteca melhora, incrementa e facilita a construção e consumo de serviços REST dentro do WCF. A grande finalidade deste novo conjunto de funcionalidades, é dar controle total ao desenvolvedor na construção de serviços REST, encapsulando toda a complexidade que o WCF possui, fornecendo classes de mais alto nível para lidar com protocolo HTTP, sua infraestrutura e as funcionalidades para qual o serviço está sendo construído.

Há algum tempo eu comentei aqui, que a Microsoft está trabalhando em um projeto para incorporar dentro do .NET Framework classes para trabalhar com serialização Json. Naquela oportunidade eu já tinha introduzido o site http://wcf.codeplex.com, que é onde a Microsoft está centralizando todas as inovações que estão sendo construídas no WCF, incluindo a Web API, que é alvo deste artigo.

Seguindo a linha de alguns outros produtos, a Microsoft também disponibilizou esta API no Nuget, ou seja, os assemblies que a compõem estão disponíveis através da adição de um packege que é acessado e instalado pelo Nuget. Ao adicionar o package WebApi.All, ele faz o download dos assemblies e já os referenciam na aplicação corrente. A imagem abaixo ilustra essa etapa:

Estrutura do Serviço

Apesar de toda a flexibilidade do WCF, uma de suas maiores reclamações é a complexidade da configuração de um serviço, e que dependendo dos recursos que você quer habilitar, a configuração pode ser ainda mais difícil. Apesar de algumas alternativas que a Microsoft criou, como é o caso do utilitário chamado WCF Service Configuration Editor, ainda assim é muito complicado, principalmente em serviços mais simples, como são os casos de serviços baseados em REST.

Essa API torna a criação de serviços REST mais simples, e a configuração para que eles funcionem quase não existe. Não que ela não seja mais necessária, mas como comentei acima, há muitas classes de mais alto nível, que encapsulam e acoplam esse serviço e sua respectiva configuração no runtime/pipeline do WCF, sem a necessidade de conhecer e gerenciar ServiceHosts, InstanceContexts, Contracts, etc. Para exemplificar, vamos criar um serviço simples, que gerenciam empresas que estão sendo prospectadas:

[ServiceContract]
public class ServicoDeProspeccao
{
    [WebGet]
    public List<Empresa> RecuperarEmpresasEmProspeccao()
    {
        //implementação
    }

    [WebInvoke]
    public void Adicionar(Empresa empresa)
    {
        //implementação
    }
}

Como podemos perceber, diferentemente de serviços que construímos até então, a classe que representa o serviço não implementa uma interface de contrato. Isso se deve ao fato de serviços REST não possuem um contrato definido, sem geração de metadados e sem operações (já que serviços REST são baseados em recursos). O atributo ServiceContractAttribute ainda continua sendo necessário, já que é esse metadado que o runtime utiliza para construir e disponibilizar o serviço para os clientes.

Os atributos WebGetAttribute e WebInvokeAttribute não são novidades, já que eles foram incluídos a partir da versão 3.5 do .NET Framework. Para recapitular, a grosso modo, WebGetAttribute permite que o acesso à operação seja feita através do método GET, enquanto o atributo WebInvokeAttribute faz com que a operação seja acessível através do método POST (padrão), PUT, etc., e você configura isso através da propriedade Method. Para mais detalhes, você pode consultar este artigo.

Utilizando a estrutura tradicional do WCF, teríamos que criar um arquivo *.svc para representar o serviço, configurar o mesmo através do Web.config, definindo os endpoints, behaviors, bindings, etc. Podemos também utilizar a classe ServiceRoute para acoplar o serviço ao sistema de roteamento de URL do ASP.NET, que apesar de trazer uma nova forma de publicar o serviço, ainda exige o conhecimento em objetos característicos do WCF. Para melhorar isso também, temos um método genérico de estensão chamado MapServiceRoute, que em um dos seus overloads, apontamos o tipo do serviço e uma string representando o prefixo. Essa configuração é realizada em nível de aplicação, e como se trata de um projeto Web, o local para colocarmos isso é o evento Application_Start, acessível através do arquivo Global.asax:

protected void Application_Start()
{
    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”);
}

Isso já é o suficiente para publicar e acessar o serviço. Baseando nas configurações acima, para acessar a primeira operação, utilizamos o seguinte endereço: http://localhost:1989/prospeccoes/RecuperarEmpresasEmProspeccao. Apenas variando o nome da operação, é o necessário para invocar a segunda operação, chamada de Adicionar.

Internamente, o método MapServiceRoute cria toda a estrutura necessária para um serviço WCF funcionar. Isso quer dizer que ele fará a criação do ServiceHost, definindo o serviço para aquela classe que foi criada acima. Além disso, a criação do endpoint é também realizada no interior deste método, e como sabemos, uma das características de um endpoint é o binding. E para alinhar com esta nova tecnologia, temos um novo binding, chamado de HttpBinding, que é utilizado por serviços que utilizam requisições e respostas baseadas em mensagens HTTP. Falaremos mais detalhadamente destes tipos adiante, ainda neste artigo.

Um detalhe importante é com relação a serialização das informações que são encaminhadas para o serviço e aquelas que são retornadas pelo mesmo. Como sabemos, serviços REST não possuem o envelope o SOAP, mas o seu conteúdo, na maioria das vezes, deve ser serializado em formato conhecido, como é o caso do Xml ou Json. Ainda no formato antigo, a Microsoft incluiu uma opção no binding WebHttpBinding chamada automaticFormatSelectionEnabled, que quando está definida como True, ela gerará o retorno de acordo com a necessidade do cliente. Mas como o cliente demonstra a sua necessidade? Basicamente o WCF irá retornar o conteúdo serializado de acordo com o que o cliente determina no header accept do HTTP. Por padrão, quando você omite, o WCF retorna o conteúdo serializado em Xml. Para o método RecuperarEmpresasEmProspeccao, temos o seguinte resultado:

<?xml version=”1.0″ encoding=”utf-8″?>
<ArrayOfEmpresa>
  <Empresa>
    <Nome>Empresa de Teste 1</Nome>
  </Empresa>
  <Empresa>
    <Nome>Empresa de Teste 2</Nome>
  </Empresa>
</ArrayOfEmpresa>

Agora, se ao definir o header accept como sendo application/json, as empresas serão retornadas em formato Json, facilitando o consumo através de aplicações Javascript:

[{“Nome”:”Empresa de Teste 1″}, {“Nome”:”Empresa de Teste 2″}]

Injeção de Dependência

Uma das boas práticas que temos hoje ao construir qualquer classe, seja ela para servir ou não como um serviço, é desenhá-las separando as responsabilidades de cada uma delas, e tornando o acoplamento entre elas cada vez menor, para que assim possamos ter um código simples de evoluir e manter.

Como o uso de serviços é comum nos dias de hoje, é importante que também se crie serviços baseando-se nestes princípios. Para isso, precisamos de um ponto de estensibilidade que permita interceptar a criação das classes que representam o serviço, e com isso, utilizarmos algum container de injeção de dependência para abastecermos todas as necessidades do serviço que está sendo exposto, para que ele esteja pronto para atender a requisição.

Quando utilizamos o WCF puro, o ponto de estensibilidade que permite isso é através da implementação da interface IInstanceProvider. Nesta nova API, temos um ponto de estensibilidade para fornecer a instância da classe que representa o serviço, e ali acoplarmos um código customizado. Para exemplificar, vamos incrementar a classe do serviço que fizemos acima (ServicoDeProspeccao), passando agora a depender de uma classe que é a implementação concreta de algum repositório. Temos dentro da classe um membro privado chamado repositorio do tipo IRepositorio. Esse repositório é utilizando no interior dos métodos RecuperarEmpresasEmProspeccao e Adicionar, carregando ou salvando os dados da implementação do repositório concreto que será injetado.

[ServiceContract]
public class ServicoDeProspeccao
{
    [Import]
    private IRepositorio repositorio;

    [WebGet]
    public List<Empresa> RecuperarEmpresasEmProspeccao()
    {
        return this.repositorio.RecuperarEmpresas();
    }

    [WebInvoke]
    public void Adicionar(Empresa empresa)
    {
        this.repositorio.Adicionar(empresa);
    }
}

Se repararmos, o membro repositorio está decorado com o atributo ImportAttribute. Esse atributo é fornecido pelo MEF (Managed Extensibility Framework), que se baseia nestes metadados para relacionar e abastecer o membro com a instância de uma classe que implementa esta interface (IRepositorio). Como exemplo, vamos criar um repositório em memória, que armazena os dados em uma lista, e também através de um atributo (ExportAttribute), indicaremos ao MEF que essa classe é uma implementação concreta da interface IRepositorio.

[Export(typeof(IRepositorio))]
public class RepositorioEmMemoria : IRepositorio
{
    public void Adicionar(Empresa empresa)
    {
        //implementação
    }

    public List<Empresa> RecuperarEmpresas()
    {
        //implementação
    }
}

Ainda é necessário configurar o container do nosso framework de injeção de dependência, que neste caso, é o MEF. Para isso, vamos recorrer a interface IResourceFactory, que está disponível por essa nova API. Essa interface fornece apenas dois métodos autoexplicativos: GetInstance e ReleaseInstance. O que fazemos no interior do método GetInstance é instanciar a classe que representa o serviço (ServicoDeProspeccao) e recorrer ao container para resolver as dependências do mesmo. Depois disso, retornamos o objeto já configurado para o WCF poder proceder com a execução da requisição.

private class MEFResourceFactory : IResourceFactory
{
    private CompositionContainer container;

    public MEFResourceFactory()
    {
        AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        this.container = new CompositionContainer(catalog);
    }

    public object GetInstance(Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
    {
        var service = Activator.CreateInstance(serviceType);
        this.container.ComposeParts(service);

        return service;
    }

    public void ReleaseInstance(InstanceContext instanceContext, object service) { }
}

Só que isso ainda não é o suficiente. Precisamos indicar ao WCF para ele recorrer à esta factory para construir o serviço. E para isso, vamos utilizar um overload do método MapServiceRoute, que além do prefixo do serviço, recebe a instância da classe HttpHostConfiguration, que representa a configuração do host do serviço, obviamente de uma forma muito mais simplificada do que aquela que é utilizada pelo WCF. Essa classe fornece um método chamado SetResourceFactory, que como o próprio nome diz, recebe ou cria a instância da classe que representa o construtor de instâncias do serviço, que no nosso caso é aquela que acabamos de criar: MEFResourceFactory. Aquela configuração que tínhamos acima no arquivo Global.asax, agora passa a figurar da seguinte forma:

protected void Application_Start(object sender, EventArgs e)
{
    var config = HttpHostConfiguration.Create().SetResourceFactory<MEFResourceFactory>();

    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”, config);
}

Trabalhando com mensagens HTTP

Como sabemos, a construção de serviços WCF possibilita criarmos um contrato e, consequentemente, uma classe que representará o serviço, que por sua vez, deve implementar aquele contrato que descreve as funcionalidades expostas por ele. Ao utilizar esta técnica, construimos nosso contrato utilizando tipos simples (inteiro, decimal, boleano, etc.) ou até mesmo tipos complexos, que são classes contendo as propriedades que levam de um lado ao outro lado a informação em uma forma mais “tipada”.

Apesar disso ser interessante, e ser um grande diferencial do WCF, isso acaba sendo um problema ao trabalhar com serviços REST. Isso se deve ao fato de que há a necessidade de ter o controle total do HTTP, que é possível com o WCF puro, mas mais uma vez, exige um conhecimento de baixo nível para conseguir manipular o protocolo e suas respectivas características.

Com o intuito de facilitar e trazer, novamente, o controle para o desenvolvedor, a Microsoft inclui nesta API classes que representam a mensagem de requisição (HttpRequestMessage) e de resposta (HttpResponseMessage). Cada uma dessas classes trazem várias propriedades, onde cada uma delas expõe características do protocolo HTTP, tais como: Content, Headers, Method, Uri, StatusCode, etc.

O serviço passará a utilizar essas classes em suas operações, ou seja, receberá um parâmetro do tipo HttpRequestMessage, que possui todas as informações necessárias solicitadas pelo cliente, enquanto o retorno será do tipo HttpResponseMessage, que será onde colocaremos todas as informações de resposta para o mesmo cliente, sendo essas informações o resultado em si, o código de status do HTTP, eventuais headers, etc. Para exemplificar, vamos analisar o método Ping abaixo:

[ServiceContract]
public class ServicoDeTeste
{
    [WebGet]
    public HttpResponseMessage Ping(HttpRequestMessage request)
    {
        return new HttpResponseMessage(HttpStatusCode.OK, string.Empty)
        {
            Content = new StringContent(“algum conteudo”)
        };
    }
}

O ponto importante a se notar é a criação da classe HttpResponseMessage, que em seu construtor receber o código de status do HTTP, e logo em seguida, temos a propriedade Content, que como já suspeitamos, deve receber o resultado da operação a ser enviada para o cliente. Há uma classe abstrata que define a estrutura de um conteúdo de retorno, chamada de HttpContent. Já existem algumas implementações concretas desta classe nesta API, e uma delas é a StringContent, que recebe uma string e será devolvida para o cliente no corpo da resposta HTTP.

E se necessitarmos enviar e/ou receber objetos mais complexos, que vão além de simples strings? Para atender esta situação, na mais nova versão desta API, a Microsoft criou versões genéricas das classes HttpRequestMessage e HttpResponseMessage, que definem como parâmetro qualquer objeto que pode/deve ser serializado na comunicação entre as partes. Para exemplificar, considere o código abaixo:

[ServiceContract]
public class ServicoDeTeste
{
    [WebInvoke]
    public HttpResponseMessage<Informacao> PingTipado(HttpRequestMessage<Informacao> request)
    {
        Informacao informacao = request.Content.ReadAs();

        informacao.Dado += ” ping”;
        informacao.Codigo += 10;

        return new HttpResponseMessage<Informacao>(HttpStatusCode.OK)
        {
            Content = new ObjectContent<Informacao>(informacao)
        };
    }
}

public class Informacao
{
    public string Dado { get; set; }
    public int Codigo { get; set; }
}

Reparem que passamos a receber um parâmetro do tipo HttpRequestMessage<Informacao>, que indica que o conteúdo da mensagem será representado pelo tipo Informacao, que possui duas propriedades. Já o resultado é definido como sendo do tipo HttpResponseMessage<Informacao>, que indica que o conteúdo da mensagem também será representado pelo tipo Informacao. O que fazemos no interior deste método é recorrer à propriedade Content da classe HttpRequestMessage<T>, que retorna um objeto do tipo ObjectContent<T>, ou seja, mais uma classe que herda de HttpContent, para definir o tipo que será recebido/enviado ao HTTP. Este objeto fornece um método chamado ReadAs, que retorna a instância da classe que representa o conteúdo da mensagem, e que no caso acima, é a classe Informacao.

Depois de extraí-la, incrementamos os valores e criamos a instância da classe HttpResponseMessage<T>, também definindo a classe Informacao como tipo T, mas nada impede de utilizar um tipo diferente para a resposta. Tudo o que fazemos ali é definirmos o status como sendo OK, que caracteriza o sucesso da operação, e definimos o conteúdo da resposta através da propriedade Content, que recebe a instância da classe ObjectContent<T>, que exigirá logo no construtor da mesma, a instância do objeto tipado em T.

Para testar, podemos enviar a seguinte requisição à operação PingTipado da seguinte forma:

User-Agent: Fiddler
Content-Type: application/xml
Host: localhost:1989
Content-Length: 62

<Informacao>
  <Dado>teste</Dado>
  <Codigo>10</Codigo>
</Informacao>

E o resultado será:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 18 Apr 2011 11:16:35 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 105
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close

<?xml version=”1.0″ encoding=”utf-8″?>
<Informacao>
  <Dado>teste ping</Dado>
  <Codigo>20</Codigo>
</Informacao>

Estou enviado e recebendo em formato Xml porque defini o header content-type como sendo application/xml, o que me devenvolverá o resultado no mesmo formato do envio. Nada impede de enviar o conteúdo em Json e definir application/json, que também me devolverá neste formato:

User-Agent: Fiddler
Content-Type: application/json
Host: localhost:1989
Content-Length: 34

{ “Dado”:”teste”, “Codigo”:123 }

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 18 Apr 2011 11:20:23 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 34
Cache-Control: private
Content-Type: application/json; charset=utf-8
Connection: Close

{“Codigo”:133,”Dado”:”teste ping”}

Conclusão: Vimos neste longo artigo a nova proposta da Microsoft para a criação de serviços WCF utilizando os princípios REST. Tudo o que vimos aqui, acaba nos bastidores utilizando todo o poder e flexibilização que o WCF fornece desde a sua primeira versão. Esta API vem com o intuito de redefinir como criamos e configuramos serviços baseados em REST, fornecendo classes de mais alto nível, sem exigir um conhecimento de toda arquitetura do WCF para saber onde e como interagir com um protocolo, que neste caso é o HTTP.

Publicidade

Deixe uma resposta

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

Logo do WordPress.com

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

Imagem do Twitter

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

Foto do Facebook

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

Conectando a %s