ASP.NET Web API - Consumo

Israel Aece

Julho 2013

Depois da API construída, hospedada e rodando no servidor, chega o momento de consumirmos isso do lado do cliente. Como o foco é o HTTP, que é bastante popular, a grande maioria das tecnologias cliente dão suporte ao consumo deste tipo de serviços. É aqui que serviços REST diferem bastante do SOAP. O SOAP define uma estrutura para comunicação, que sem um ferramental que ajude, o consumo se torna bastante complexo, uma vez que temos que manualmente formatar as mensagens de envio e interpretar as mensagens de retorno.

Mesmo com poucos facilitadores para consumir serviços por parte de uma tecnologia, o trabalho com serviços HTTP não é muito complexo. Com um objeto que gerencie a conexão, a configuração da requisição e leitura da resposta acabo sendo uma tarefa bem mais simples do que quando comparado ao SOAP.

Podemos ter os mais variados tipos de clientes, podendo ser aplicações desktops, de linha de comando, aplicações para internet, dispositivos móveis ou até mesmo outros serviços. Quando o consumo é realizado por aplicações Web, é muito comum utilizarmos código script para isso, onde recorremos à Javascript para solicitar o envio, recuperar a resposta e apresentar o resultado na tela. Neste ambiente temos uma biblioteca chamada jQuery, que fornece uma infinidade de facilitadores para o consumo de serviços REST.

Além disso, podemos consumir serviços REST em aplicações que não são Web. Da mesma forma que a Microsoft criou tudo o que vimos até o momento para a construção de serviços, ela também se preocupou em criar recursos para tornar o consumo de serviços REST em aplicações .NET muito simples.

Para consumirmos serviços REST em aplicações .NET, o primeiro passo é referenciar o assembly System.Net.Http.dll. Dentro deste assembly temos o namespace System.Net.Http, que possui tipos para consumir serviços baseados exclusivamente no protocolo HTTP. Os componentes que veremos aqui podem também ser utilizados pelo lado do serviço (como é o caso das classes HttpRequestMessage e HttpResponseMessage), fornecendo uma certo simetria entre o código do lado do cliente e do lado do serviço, obviamente quando ambos forem .NET.

Só que este assembly apenas não é o suficiente. Ao invés de descobrirmos quais os assemblies e recursos que precisamos, podemos recorrer ao Nuget para que ele registre tudo o que precisamos nas aplicações cliente para consumir serviços HTTP utilizando o ASP.NET Web API. Para isso podemos digitar a chave “Microsoft.AspNet.WebApi.Client”, e já teremos a Microsoft ASP.NET Web API Client Libraries listada nos resultados, e clicando no botão Install, elas são configuradas no projeto em questão.

É importante dizer que também temos a versão portável destas bibliotecas. Com isso, poderemos reutilizar o consumo de APIs REST (HTTP) através de diferentes plataformas, sem a necessidade de reconstruir a cada novo projeto uma ponte de comunicação com estes tipos de serviços, e ainda, sem a necessidade de recorrer de classes de mais baixo nível para atingir o mesmo objetivo.

Dn376305.052A19032BB9ED017241180FF1C899DC(pt-br,MSDN.10).png

Figura 13 - Pacote com recursos para consumo de APIs.

Do lado do cliente a principal classe é a HttpClient. Ela é responsável por gerenciar toda a comunicação com um determinado serviço. Ela atua como sendo uma espécie de sessão, onde você pode realizar diversas configurações e que serão aplicadas para todas as requisições que partirem da instância desta classe.

Essa é a principal classe que é utilizada quando estamos falando de um cliente .NET, fornecendo métodos nomeados com os principais verbos do HTTP, e que trabalham com as classes HttpRequestMessage e HttpResponseMessage, o que dará ao cliente o controle total da mensagem que é enviada e da resposta que é retornada. Internamente a classe HttpClient trabalha com classes que são velhas conhecidas de quem já trabalha com .NET: HttpWebRequest e HttpWebResponse, mas como todo e qualquer facilitador, a classe HttpClient encapsula toda a complexidade destas classes, expondo métodos mais simples para consumo dos serviços.

Apesar da classe HttpClient ser comparada com um proxy de um serviço WCF, você não está obrigado a somente invocar APIs que estão debaixo de um mesmo servidor, pois isso deve ser definido o endereço (URL) onde está localizado o serviço. Caso quisermos criar um cliente específico para um determinado serviço, podemos herdar da classe HttpClient, e fornecer métodos específicos para acessar os recursos que são manipulados pela API. Um exemplo disso seria ter uma classe chamada FacebookClient, que internamente recorrerá aos métodos da classe HttpClient encapsulando ainda mais a – pouca – complexidade que temos, tornando o consumo um pouco amigável.

Antes de analisarmos algum código, é importante dizer que a classe HttpClient expõe os métodos para consumo dos serviços de forma assíncrona. Como o consumo depende de recursos de I/O, os métodos foram desenhados para que ao consumir qualquer recurso remoto, essa tarefa seja executada por uma outra thread. E, como ele já faz uso do novo mecanismo de programação assíncrona do .NET (Tasks), ficar bastante simples gerenciar e coordenar requisições que são realizadas.

class Program
{
    private const string Endereco = "https://localhost:43139/api/artistas/MaxPezzali";

    static void Main(string[] args)
    {
        Executar();
        Console.ReadLine();
    }

    private async static void Executar()
    {
        using (var client = new HttpClient())
        {
            using (var request = await client.GetAsync(Endereco))
            {
                request.EnsureSuccessStatusCode();

                await request.Content.ReadAsAsync<JObject>().ContinueWith(t =>
                {
                    Console.WriteLine(t.Result["Nome"]);
                });
            }
        }
    }
}

Dentro do método Executar criamos a instância da classe HttpClient, que através do método GetAsync, realiza uma requisição através do verbo GET para o serviço de artistas, que estamos utilizando para os exemplos. Temos que fazer uso das palavras async e await para que o C# possa criar o mecanismo necessário para que o serviço seja invocada de forma assíncrona.

O método EnsureSuccessStatusCode assegura que o serviço foi invocado com sucesso, disparando uma exceção se o código do status de retorno caracterizar uma falha. Finalmente chamando o método, também de forma assíncrona, ReadAsAsync<T>. Este método realiza a leitura do corpo da mensagem de retorno, tentando converter a mesma no tipo genérico T. No exemplo acima estamos utilizando a classe JObject, que é fornecida pelo framework Json.NET, que é o mais popular meio para manipular a serialização de objetos em Json dentro do .NET. Para instalarmos, basta recorrer ao comando abaixo no Nuget:

    PM> Install-Package Newtonsoft.Json

Finalmente, quando finalizamos a leitura do corpo da mensagem, entregamos o objeto JObject ao ContinueWith para que ele exiba na tela. O objeto JObject implementa a interface IDictionary, que dado uma string com o nome da propriedade, ele retorna o seu valor.

O problema da técnica acima é que ela é baseada em strings e objects, sem qualquer tipificação, o que pode gerar erros, e que na maioria das vezes, acontecem somente durante a execução. Para facilitar isso, o Visual Studio .NET 2012, fornece dois recursos chamados “Paste JSON as Classes” e “Paste XML as Classes”, que dado um conteúdo Json ou Xml que está na área de transferência, ele consegue construir as classes no projeto, configurando suas propriedades e seus respectivos tipos.

Dn376305.13A7080331498E075EBD64A7B52058B2(pt-br,MSDN.10).png

Figura 14 -Opções do Visual Studio.

public class Rootobject
{
    public int Id { get; set; }
    public string Nome { get; set; }
    public int AnoDeNascimento { get; set; }
    public int QuantidadeDeAlbuns { get; set; }
}

Desta forma, podemos passar a utilizar as classes específicas ao invés de ficar lidando com índices e nome das propriedades, que acaba sendo sujeito a erros. O código abaixo exibe um trecho do consumo do serviço recorrendo a classe que acabamos de construir:

await request.Content.ReadAsAsync<Artista>().ContinueWith(t =>
{
    Console.WriteLine(t.Result.Nome);
});

Como pudemos perceber, o método GetAsync recebe apenas o endereço para onde a requisição deve ser realizada. Mas e se quisermos customizar a requisição? Por exemplo, incluir headers específicos para caching e segurança, especificar o formato de resultado que desejamos, etc. Devido a essas necessidades é que devemos recorrer a construção e configuração da classe HttpRequestMessage. É através dela que toda a configuração é realizada antes da mesma partir para o serviço, e depois que ela estiver devidamente configurada, recorremos ao método SendAsync, que como parâmetro recebe a classe HttpRequestMessage e retorna a classe HttpResponseMessage. Apesar de Send não refletir um verbo do HTTP, é dentro da mensagem que vamos especificar qual verbo será utilizado para realizar a requisição.

Utilizando o mesmo exemplo acima, vamos customizar a requisição para que ela retorne o artista em formato Xml:

using (var client = new HttpClient())
{
    using (var request = new HttpRequestMessage(HttpMethod.Get, Endereco))
    {
        request.Headers.Add("Accept", "application/xml");

        using (var response = await client.SendAsync(request))
            Console.WriteLine(response.Content.ReadAsStringAsync().Result);
    }
}

A classe HttpMethod traz algumas propriedades estáticas com os principais verbos criados, prontos para serem utilizados. Logo na sequência estamos adicionando um novo header chamado Accept, que determina o formato que queremos que o retorno seja devolvido. Da mesma forma que podemos utilizar a classe HttpRequestMessage para customizar a requisição, podemos inspecionar a classe HttpResponseMessage para explorarmos tudo aquilo que o serviço nos devolveu e, consequentemente, tomar decisões, realizar logs, novas requisições, etc.

Tanto a classe HttpRequestMessage quanto a HttpResponseMessage implementam a interface IDisposable, e por isso, podem ser envolvidas em blocos using. Ambas as classes fornecem uma propriedade chamada Content, onde temos o corpo das mensagens, e como ele pode ser um stream, a implementação dessa interface auxilia no descarte imediato quando elas não estão mais sendo utilizadas.

Quando vamos utilizar um verbo diferente do GET, como é o caso do POST, então precisamos definir o corpo da mensagem de envio, onde temos várias opções para os mais diversos conteúdos que enviamos para o servidor. O diagrama abaixo dá uma dimensão disso.

Dn376305.1F83D4C3B206787278B4D6F5C76E3DF5(pt-br,MSDN.10).png

Figura 15 - Hierarquia das classes que representam o conteúdo das mensagens.

Cada uma dessa classes lidam exclusivamente com um tipo de conteúdo. Temos como customizar o envio determinando que o corpo deve ser uma espécie de formulário, string, stream e objetos.

Independente se estamos falando de requisição ou resposta, a classe ObjectContent<T> tem a função de materializar objetos, baseando-se no formato que ele foi serializado. Existem classes que representam esses formatos, quais falaremos delas mais adiante, mas quando estamos consumindo o serviço através desta biblioteca, não precisamos lidar diretamente com elas, pois existem alguns facilitadores que abstraem essa complexidade, e os formatadores apenas farão o trabalho (bidirecional) sobre os bytes, o que de fato é o que trafega entre as partes.

private const string Endereco = "https://localhost:43139/api/artistas/Adicionar";

private async static void Executar()
{
    using (var client = new HttpClient())
    {
        await client.PostAsync(Endereco, new ObjectContent<Artista>(new Artista()
        {
            Nome = "PaoloMeneguzzi"
        }, new JsonMediaTypeFormatter())).ContinueWith(t =>
        {
            Console.WriteLine(t.Result.StatusCode);
        });
    }
}

No exemplo que temos acima, estamos recorrendo ao método PostAsync, para que o objeto (classe Artista) seja postado. Neste caso, temos que explicitamente mencionar que queremos que o conteúdo seja serializado através de Json. Felizmente, graças à algumas extensões que já temos no ASP.NET Web API, podemos tornar esse código menos verboso:

private const string Endereco = "https://localhost:43139/api/artistas/Adicionar";

private async static void Executar()
{
    using (var client = new HttpClient())
    {
        await client.PostAsJsonAsync(Endereco, 
            new Artista() { Nome = "PaoloMeneguzzi" }).ContinueWith(t =>
        {
            Console.WriteLine(t.Result.StatusCode);
        });
    }
}

Da mesma forma que temos do lado do serviço, a classe HttpClient também possui um overload do construtor que recebe a instância de uma classe do tipo HttpMessageHandler, qual podemos utilizar para interceptar a requisição e a resposta, injetando algum código customizado, como por exemplo, efetuar o log das mensagens, inspecionar os headers, etc. No capítulo sobre estensibilidade será abordado as opções disponíveis que temos para isso.

Veja também:

ASP.NET Web API – HTTP, REST e o ASP.NET: Para basear todas as funcionalidades expostas pela tecnologia, precisamos ter um conhecimento básico em relação ao que motivou tudo isso, contando um pouco da história e evolução, passando pela estrutura do protocolo HTTP e a relação que tudo isso tem com o ASP.NET.

ASP.NET Web API – Estrutura da API: Entenderemos aqui a template de projeto que o Visual Studio fornece para a construção das APIs, bem como sua estrutura e como ela se relaciona ao protocolo.

ASP.NET Web API – Roteamento: Como o próprio nome diz, o capítulo irá abordar a configuração necessária para que a requisição seja direcionada corretamente para o destino solicitado, preenchendo e validando os parâmetros que são por ele solicitado.

ASP.NET Web API – Hosting: Um capítulo de extrema relevância para a API. É o hosting que dá vida à API, disponibilizando para o consumo por parte dos clientes, e a sua escolha interfere diretamente em escalabilidade, distribuição e gerenciamento. Existem diversas formas de se expor as APIs, e aqui vamos abordar as principais delas.

ASP.NET Web API – Consumo: Como a proposta é ter uma API sendo consumido por qualquer cliente, podem haver os mais diversos meios (bibliotecas) de consumir estas APIs. Este capítulo tem a finalidade de exibir algumas opções que temos para este consumo, incluindo as opções que a Microsoft criou para que seja possível efetuar o consumo por aplicações .NET.

ASP.NET Web API – Formatadores: Os formatadores desempenham um papel importante na API. São eles os responsáveis por avaliar a requisição, extrair o seu conteúdo, e quando a resposta é devolvida ao cliente, ele entra em ação novamente para formatar o conteúdo no formato em que o cliente possa entender. Aqui vamos explorar os formatadores padrões que já estão embuitdos, bem como a criação de um novo.

ASP.NET Web API – Segurança: Como a grande maioria das aplicações, temos também que nos preocupar com a segurança das APIs. E quando falamos de aplicações distribuídas, além da autenticação e autorização, é necessário nos preocuparmos com a segurança das mensagens que são trocadas entre o cliente e o serviço. Este capítulo irá abordar algumas opções que temos disponíveis para tornar as APIs mais seguras.

ASP.NET Web API – Testes e Tracing: Para toda e qualquer aplicação, temos a necessidade de escrever testes para garantir que a mesma se comporte conforme o esperado. Isso não é diferentes com APIs Web. Aqui iremos abordar os recursos, incluindo a própria IDE, para a escrita, gerenciamento e execução dos testes.

ASP.NET Web API – Estensibilidade e Arquitetura: Mesmo que já tenhamos tudo o que precisamos para criar e consumir uma API no ASP.NET Web API, a customização de algum ponto sempre acaba sendo necessária, pois podemos criar mecanismos reutilizáveis, “externalizando-os” do processo de negócio em si. O ASP.NET Web API foi concebido com a estensibilidade em mente, e justamente por isso que existe um capítulo exclusivo para abordar esse assunto.

| Home | Artigos Técnicos | Comunidade