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.