O desenvolvimento de novas soluções Web nos dias atuais está associado, na esmagadora maioria dos casos, ao uso de jQuery em atividades relacionadas à implementação de interfaces gráficas. Baseando-se totalmente em JavaScript, jQuery surgiu com o objetivo de simplificar a manipulação de HTML em sites (evitando a codificação de instruções muito extensas), além de oferecer uma alternativa que contasse com um grau mínimo de compatibilidade com os principais browsers do mercado.

Surgida ainda em 2006, a biblioteca jQuery encontra-se neste momento na versão 2.0.0 (Maio/2013). Grandes portais como Wikipédia e o WordPress representam exemplos notórios de uso da mesma. A própria Microsoft tem estimulado a utilização desta biblioteca: em versões mais recentes do Visual Studio (2010 e 2012), aplicações ASP.NET Web Forms e MVC criadas através de templates contam com versões de jQuery como parte de seu conteúdo.

Além da execução de operações sobre os elementos HTML em páginas da Web, jQuery também disponibiliza mecanismos para a implementação de eventos, animações e funcionalidades empregando recursos de AJAX (sigla do inglês “Asynchronous JavaScript and XML”). A flexibilidade que se conseguiu por meio desta biblioteca serviu de base, inclusive, para o lançamento de diversos plugins, controles e frameworks voltados à construção de interfaces gráficas.

Como não é difícil de imaginar, muitas dessas soluções concebidas em jQuery (como gráficos, grids e painéis de controle) utilizam dados em formatos como XML ou JSON (abreviação do inglês “JavaScript Object Notation”), os quais foram obtidas a partir de algum repositório. Tais fontes de informações podem ser representadas tanto por funcionalidades dentro uma aplicação que faz uso de recursos em jQuery, quanto por Web Services acessados de maneira remota.

A própria plataforma .NET permite, através da tecnologia WCF (Windows Communication Foundation), a construção de serviços que serão consumidos via instruções de código em jQuery/JavaScript. O objetivo deste artigo é demonstrar justamente como tais Web Services são implementados e, posteriormente, um exemplo em jQuery de acesso aos dados (no formato JSON) disponibilizados por estes componentes.

Criando o serviço WCF de exemplo

A solução que está sendo apresentada neste artigo foi criada no .NET Framework 4.5, através da utilização do Microsoft Visual Studio 2012 Professional. Inicialmente será criado o projeto TesteWCFJSON, o qual é baseado no template “WCF Service Application” (conforme indicado na Figura 1).

Criando o projeto TesteWCFJSON
Figura 1. Criando o projeto TesteWCFJSON

Basicamente, será construído um serviço que retornará informações anuais sobre as exportações e importações de uma indústria fictícia. Tais dados poderão estar organizados tanto por país, quanto por continente.

Como primeiro passo na implementação do projeto TesteWCFJSON, excluir os arquivos Service1.svc e IService1.cs (os mesmos foram gerados automaticamente ao se escolher o template “WCF Service Application”).

Em seguida, deverão ser implementadas as classes BalancoComercialPorPais e BalancoComercialPorContinente (Listagem 1). O tipo BalancoComercialPorPais corresponde ao resumo de exportações e importações para um país num determinado ano, ao passo que instâncias de BalancoComercialPorContinente possuem um conteúdo similar, mas com o agrupamento de informações por continentes.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;

namespace TesteWCFJSON
{
    [DataContract]
    public class BalancoComercialPorPais
    {
        [DataMember]
        public int AnoBase { get; set; }

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

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

        [DataMember]
        public double ValorExportado { get; set; }

        [DataMember]
        public double ValorImportado { get; set; }
    }

    [DataContract]
    public class BalancoComercialPorContinente
    {
        [DataMember]
        public int AnoBase { get; set; }

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

        [DataMember]
        public double ValorExportado { get; set; }

        [DataMember]
        public double ValorImportado { get; set; }
    }
}
Listagem 1. Classes BalancoComercialPorPais e BalancoComercialPorContinente

Estas duas classes (BalancoComercialPorPais e BalancoComercialPorContinente) representam bons exemplos de um tipo de elemento conhecido dentro de WCF como Data Contract. Em termos gerais, um Data Contract nada mais é do que um tipo complexo, ou seja, uma estrutura composta pela combinação de tipos primitivos ou mesmo outras construções mais elaboradas (referências que apontem para outras classes). Data Contracts podem ser empregados tanto como parâmetros de entrada em operações de um serviço, quanto como valores retornados após a execução de tais métodos.

Para ser considerada um Data Contract, uma classe precisa ter associada à sua definição um atributo DataContractAttribute (namespace System.Runtime.Serialization).

É importante fazer ainda uma observação quanto ao uso do tipo DataContractAttribute: por convenção dentro da plataforma .NET, nomes de classes que representam atributos terminam com o sufixo Attribute. Instruções que envolvam um atributo vinculado a uma estrutura de código dispensam o uso de tal sufixo ao final do nome. Logo, ao se utilizar o atributo DataContractAttribute, emprega-se apenas “DataContract” na construção a ser marcada com este elemento.

Além da própria definição da classe, as propriedades de um Data Contract que serão expostas via serviço também deverão ter um atributo ligado às mesmas, fazendo-se uso neste caso do tipo DataMemberAttribute (pertencente ao namespace System.Runtime.Serialization).

Pelo fato do projeto TesteWCFJSON ter como objetivo a realização de testes envolvendo a utilização de serviços WCF que devolvam informações no formato JSON, esta aplicação não fará uso de um banco de dados relacional. Devido a este motivo, serão criadas duas classes estáticas (SimulacaoBalanco2011 e SimulacaoBalanco2012) que retornarão valores fictícios, com cada uma dessas estruturas produzindo como resultado dados de um ano específico.

Na Listagem 2 está a definição da classe SimulacaoBalanco2011. O método ObterBalancoPaises será utilizado para se ter acesso ao balanço comercial de diferentes países, enquanto a operação ObterBalancoContinentes provê o mesmo tipo de informação por continentes.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TesteWCFJSON
{
    public static class SimulacaoBalanco2011
    {
        public static List<BalancoComercialPorPais> ObterBalancoPaises()
        {
            List<BalancoComercialPorPais> dados =
                new List<BalancoComercialPorPais>();

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "Alemanha",
                Sigla = "DE",
                ValorExportado = 35.1,
                ValorImportado = 15.5
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "Canadá",
                Sigla = "CA",
                ValorExportado = 15.6,
                ValorImportado = 4.9
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "China",
                Sigla = "CN",
                ValorExportado = 45.3,
                ValorImportado = 30.2
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "Estados Unidos",
                Sigla = "US",
                ValorExportado = 50.4,
                ValorImportado = 25.3
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "Japão",
                Sigla = "JP",
                ValorExportado = 40.2,
                ValorImportado = 20.4
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2011,
                Pais = "Reino Unido",
                Sigla = "GB",
                ValorExportado = 30.9,
                ValorImportado = 9.7
            });

            return dados;
        }

        public static List<BalancoComercialPorContinente> ObterBalancoContinentes()
        {
            List<BalancoComercialPorContinente> dados =
                new List<BalancoComercialPorContinente>();

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2011,
                Continente = "América",
                ValorExportado = 66,
                ValorImportado = 30.2
            });

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2011,
                Continente = "Ásia",
                ValorExportado = 85.5,
                ValorImportado = 50.6
            });

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2011,
                Continente = "Europa",
                ValorExportado = 66,
                ValorImportado = 25.2
            });

            return dados;
        }
    }
}
Listagem 2. Classe SimulacaoBalanco2011

Já na Listagem 3 está o código que implementa o tipo SimulacaoBalanco2012. Conforme pode ser observado, a estrutura aqui descrita é idêntica à da classe SimulacaoBalanco2011, diferindo apenas pelo conteúdo das informações retornadas.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TesteWCFJSON
{
    public static class SimulacaoBalanco2012
    {
        public static List<BalancoComercialPorPais> ObterBalancoPaises()
        {
            List<BalancoComercialPorPais> dados =
                new List<BalancoComercialPorPais>();

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "Alemanha",
                Sigla = "DE",
                ValorExportado = 38.6,
                ValorImportado = 20.2
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "Canadá",
                Sigla = "CA",
                ValorExportado = 17.2,
                ValorImportado = 3.4
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "China",
                Sigla = "CN",
                ValorExportado = 49.8,
                ValorImportado = 36.2
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "Estados Unidos",
                Sigla = "US",
                ValorExportado = 55.4,
                ValorImportado = 27.8
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "Japão",
                Sigla = "JP",
                ValorExportado = 44.2,
                ValorImportado = 18.4
            });

            dados.Add(new BalancoComercialPorPais()
            {
                AnoBase = 2012,
                Pais = "Reino Unido",
                Sigla = "GB",
                ValorExportado = 34,
                ValorImportado = 7.8
            });

            return dados;
        }

        public static List<BalancoComercialPorContinente> ObterBalancoContinentes()
        {
            List<BalancoComercialPorContinente> dados =
                new List<BalancoComercialPorContinente>();

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2012,
                Continente = "América",
                ValorExportado = 72.6,
                ValorImportado = 31.2
            });

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2012,
                Continente = "Ásia",
                ValorExportado = 94,
                ValorImportado = 54.6
            });

            dados.Add(new BalancoComercialPorContinente()
            {
                AnoBase = 2012,
                Continente = "Europa",
                ValorExportado = 72.6,
                ValorImportado = 28
            });

            return dados;
        }
    }
}
Listagem 3. Classe SimulacaoBalanco2012

Concluindo agora a implementação do projeto TesteWCFJSON, será necessário criar um serviço de nome BalancoComercialWS, conforme demonstrado na Figura 2.

Criando o serviço BalancoComercialWS
Figura 2. Criando o serviço BalancoComercialWS

Como resultado disto, serão criados os seguintes elementos:

  • A interface IBalancoComercialWS, a qual corresponde ao contrato que define o serviço de exemplo;
  • A classe BalancoComercialWS, responsável por implementar as funcionalidades oferecidas pelo serviço de exemplo.

Na interface IBalancoComercialWS (Listagem 4) estão declaradas as operações que serão expostas através do serviço WCF abordado neste artigo. IBalancoComercialWS foi marcada com o atributo ServiceContractAttribute (namespace System.ServiceModel): este é um pré-requisito para se expor uma interface (ou mesmo uma classe) como um serviço quando se emprega a tecnologia WCF.

Além disso, é possível observar nas declarações das operações ObterBalancoPaises e ObterBalancoContinenetes:

  • O uso do atributo OperationContractAttribute (namespace System.ServiceModel), o qual deve estar associado a métodos que poderão ser invocados por consumidores de um serviço;
  • A presença do atributo WebGetAttribute (namespace System.ServiceModel.Web), característica esta que permite a aplicações-cliente acessar as operações de um serviço via requisições HTTP do tipo GET.

Ainda sobre a utilização do atributo WebGetAttribute, ao se definirem as operações da interface IBalancoComercialWS foram preenchidas as seguintes propriedades:

  • ResponseFormat: indica o formato de dados a ser utilizado no retorno do serviço (JSON ou XML), baseando-se para isto no enumeration WebMessageFormat (namespace System.ServiceModel.Web);
  • UriTemplate: permite customizar a URL para acesso a um método que faz parte um serviço. Os diferentes parâmetros esperados por tal operação devem estar entre "chaves" (“{”e “}”}, bem como especificados como sendo do tipo string.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace TesteWCFJSON
{
    [ServiceContract]
    public interface IBalancoComercialWS
    {
        [OperationContract]
        [WebGet(
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "paises/{anoBase}")]
        List<BalancoComercialPorPais> ObterBalancoPaises(
            string anoBase);

        [OperationContract]
        [WebGet(
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "continentes/{anoBase}")]
        List<BalancoComercialPorContinente> ObterBalancoContinentes(
            string anoBase);
 
    }
}
Listagem 4. Interface IBalancoComercialWS

A classe BalancoComercialWS (Listagem 5) implementa a interface IBalancoComercialWS, sendo responsável por processar as requisições enviadas ao serviço WCF de exemplo.

Conforme é possível observar na definição de BalancoComercialWS, este tipo faz uso das classes SimulacaoBalanco2011 e SimulacaoBalanco2012 para a obtenção dos dados de simulação referentes aos de 2012 e 2012 respectivamente. Se for informado outro ano, uma exceção do tipo FaultException será gerada a partir dos métodos ObterBalancoPaises e ObterBalancoContinentes.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace TesteWCFJSON
{
    public class BalancoComercialWS : IBalancoComercialWS
    {
        public List<BalancoComercialPorPais> ObterBalancoPaises(
            string anoBase)
        {
            int ano;
            if (String.IsNullOrWhiteSpace(anoBase) ||
                !int.TryParse(anoBase, out ano))
            {
                throw new FaultException(
                    "É obrigatório informar um ano-base válido.");
            }

            if (ano == 2011)
                return SimulacaoBalanco2011.ObterBalancoPaises();
            else if (ano == 2012)
                return SimulacaoBalanco2012.ObterBalancoPaises();
            else
            {
                throw new FaultException(
                    "O ano-base informado é inválido.");
            }
        }

        public List<BalancoComercialPorContinente> ObterBalancoContinentes(
            string anoBase)
        {
            int ano;
            if (String.IsNullOrWhiteSpace(anoBase) ||
                !int.TryParse(anoBase, out ano))
            {
                throw new FaultException(
                    "É obrigatório informar um ano-base válido.");
            }

            if (ano == 2011)
                return SimulacaoBalanco2011.ObterBalancoContinentes();
            else if (ano == 2012)
                return SimulacaoBalanco2012.ObterBalancoContinentes();
            else
            {
                throw new FaultException(
                    "O ano-base informado é inválido.");
            }
        }
    }
}
Listagem 5. Classe BalancoComercialWS

A Listagem 6 apresenta o arquivo Web.config, o qual contém as configurações que permitem que o serviço WCF seja acessado por aplicações consumidoras.

A configuração de um serviço acontece dentro de um elemento chamado "service", o qual pertence ao agrupamento "services" da seção "system.serviceModel".

No elemento “service” é possível destacar as seguintes configurações relativas ao serviço BalancoComercialWS:

  • O atributo “name”, no qual consta o nome completo (incluindo namespace) do tipo que implementa o Web Service;
  • O elemento endpoint, em que estão referências a outros itens dentro do próprio arquivo Web.config, além dos atributos “contract” (interface do serviço) e binding (no caso, definiu-se “webHttpBinding” para o serviço em questão).

Dentro do elemento service é possível notar ainda a existência do endereço a ser adotado como endpoint, o qual foi declarado como valor do atributo “baseAddress”. Será a partir deste caminho que outros sistemas conseguirão acessar um Web Service.

As configurações relativas à utilização do binding webHttpBinding encontram-se declaradas em um elemento de mesmo nome. Importante destacar o atributo “crossDomainScriptAccessEnabled” marcado como “true”; este ajuste permite que instruções em JavaScript/jQuery consigam interagir com o serviço de testes (se isto não fosse feito um erro seria gerado, impedindo à aplicação que testará o Web Service consumir os dados disponibilizados pelo mesmo).

O elemento “endpointBehaviors” é o local em que estão sendo configuradas as definições para o endpoint do serviço aqui descrito. A existência do elemento “webHttp” permite a tal endpoint a recepção e o tratamento de requisições HTTP.


<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext"
         value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="TesteWCFJSON.BalancoComercialWS"
               behaviorConfiguration="serviceBehaviorBalanco">
        <endpoint address=""
                  behaviorConfiguration="endpointBehaviorBalanco"
                  binding="webHttpBinding"
                  bindingConfiguration="bindingBalanco"
                  contract="TesteWCFJSON.IBalancoComercialWS" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:55486/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <bindings>
      <webHttpBinding>
        <binding name="bindingBalanco"
                 crossDomainScriptAccessEnabled="true">
          <security mode="None" />
        </binding>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="endpointBehaviorBalanco">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="serviceBehaviorBalanco">
          <serviceMetadata httpGetEnabled="true"
                           httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true"
                           httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
                               multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>
Listagem 6. Arquivo Web.config da aplicação TesteWCFJSON

Um teste rápido pode ser feito neste momento, com intuito de verificar se a implementação do serviço BalancoComerciaWS ocorreu conforme o planejado. Para isto, acessar em um browser a URL:


http://localhost:55486/BalancoComercialWS.svc/paises/2012

Na Figura 3 está o resultado esperado (ao se utilizar o navegador Google Chrome). Conforme é possível observar, as informações retornadas estão no formato JSON, o qual nada mais é do que uma sequência de texto em que constam pares de informações. Cada um destes pares possui um identificador (string que determina um nome de propriedade), além de seu respectivo valor; os dados correspondentes a cada objeto estão separados por “chaves” (“{” e “}”).

Acessando o Web Service BalancoComercialWS via Google Chrome
Figura 3. Acessando o Web Service BalancoComercialWS via Google Chrome

Consumindo o serviço de exemplo

Para se testar o serviço WCF implementado na seção anterior, será necessário criar uma aplicação ASP.NET Web Forms chamada TesteWCFJSON.ConsumidorWebForms (Figura 4).

Criando o projeto TesteWCFJSON.ConsumidorWebForms
Figura 4. Criando o projeto TesteWCFJSON.ConsumidorWebForms

Os dados retornados pelas operações do Web Service BalancoComercialWS serão utilizados para a geração de gráficos, empregando para esta tarefa a biblioteca Google Chart Tools.

A biblioteca Google Chart Tools é um conjunto de recursos disponibilizado gratuitamente pela Google, tendo por objetivo fornecer alternativas para a geração de gráficos dos mais variados tipos de uma forma simples e flexível. Diversas opções permitem que a aparência de um gráfico possa ser customizada, além deste framework JavaScript possuir compatibilidade com os principais browsers e plataformas do mercado (Internet Explorer, Firefox, Chrome, Android, iOS). Importante mencionar ainda que esta biblioteca também possibilita a representação de dados sob a forma de tabelas.

Com o projeto Web Forms gerado, incluir uma nova página na aplicação que terá por nome BalancoContinentes.aspx (esse formulário servirá de base para a exibição de informações sobre exportações/importações por continente); isto pode ser feito clicando-se com o botão direito do mouse sobre o projeto TesteWCFJSON.ConsumidorWebForms e selecionando a opção “Add > Web Form using Master Page”.

Na Listagem 7 está o código que implementa o formulário BalancoContinentes.aspx. A partir desta página será possível a visualização de um gráfico com os percentuais de importação e exportação por continente.

A geração de gráficos com o Google Chart Tools a partir de dados disponibilizados por um serviço WCF engloba os seguintes procedimentos:

  • O preenchimento da propriedade cors do objeto support de jQuery como “true”, a fim de tornar possível o envio de requisições ao Web Service de exemplo;
  • O acionamento do método load do objeto google, carregando assim as bibliotecas para geração de gráficos;
  • Criação de funções que serão invocadas a partir do método setOnLoadCallback do objeto google (gerarVisualizacaoGraficoExportacoes e gerarVisualizacaoGraficoImportacoes).

Conforme é possível observar, os métodos gerarVisualizacaoGraficoExportacoes e gerarVisualizacaoGraficoImportacoes estão acessando a função gerarVisualizacao. Esta última recebe como parâmetros o tipo de gráfico a ser gerado, além do id do controle HTML em que será renderizado o gráfico (normalmente uma div).

Na implementação de gerarVisualizacao estão sendo efetuadas as seguintes ações:

  • Criação de um Data Table, através da geração de uma nova instância do tipo google.visualization.DataTable. As colunas da tabela resultante são configuradas através do método addColumn (que recebe como parâmetros o tipo e a descrição do campo);
  • Invocação via instrução jQuery do método do Web Service que irá fornecer o balanço de um determinado ano no formato JSON. Por meio da utilização do comando $.ajax, uma operação definida no serviço WCF é acionada (de forma síncrona, ou seja, aguardando a execução total do método, com isto sendo indicado no parâmetro async). O acesso à operação ObterBalancoContinentes acontece por meio de uma URL em que consta um ano de referência (fornecido através do parâmetro “ano” em uma Query String), além do endereço para utilização de tal método (configurado via atributo WebGetAttribute no Web Service);
  • Os dados retornados pelo Web Service são convertidos para um array, empregando para isto a instrução $.each (a qual permite navegar a cada objeto JSON obtido, convertendo os dados do mesmo para a matriz no formato esperado pelo Data Table);
  • Um objeto do tipo google.visualization.NumberFormat é gerado, a fim de se proceder com a formatação de valores monetários;
  • A variável options está sendo preenchida com configurações que determinam a aparência com o gráfico será renderizado;
  • A geração de uma instâcia do tipo google.visualization.PieChart (gráfico de "pizza") acontece associando-se à mesma a div HTML correspondente. Ao se acionar o método draw (fornecendo como parâmetros o Data Table e as opções de configuração) a representação gráfica esperada será finalmente montada.

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master"
    AutoEventWireup="true" CodeBehind="BalancoContinentes.aspx.cs"
    Inherits="TesteWCFJSON.ConsumidorWebForms.BalancoContinentes" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent"
    runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent"
    runat="server">
    <script type="text/javascript"
        src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
 
        function gerarVisualizacao(tipo, idControle) {
            var ano = '<%: Request.QueryString["ano"] %>';
 
            var data = new google.visualization.DataTable();
            data.addColumn('string', 'Continente');
            data.addColumn('number', 'Valor');
 
            var urlBalanco = 'http://localhost:55486/' +
                'BalancoComercialWS.svc/continentes/' + ano;
 
            var retornoWS;
            $.ajax(
            {
                type: 'GET',
                url: urlBalanco,
                dataType: 'json',
                crossDomain: true,
                async: false,
                success: function (data) {
                    retornoWS = data;
                }
            });
 
            var dadosBalanco = [];
            $.each(retornoWS, function (i, balanco) {
                if (tipo == 'EXPORTACOES') {
                    dadosBalanco.push([
                        balanco.Continente.toString(),
                        parseFloat(balanco.ValorExportado)]);
                } else {
                    dadosBalanco.push([
                        balanco.Continente.toString(),
                        parseFloat(balanco.ValorImportado)]);
                }
            });
            data.addRows(dadosBalanco);
 
            var formatter = new google.visualization.NumberFormat(
                {
                    prefix: 'R$ (milhões) ',
                    decimalSymbol: ',',
                    groupingSymbol: '.',
                    fractionDigits: 1
                });
            formatter.format(data, 1);
 
            var titulo;
            if (tipo == 'EXPORTACOES')
                titulo = 'Exportações por Continente - ' + ano;
            else
                titulo = 'Importações por Continente - ' + ano;
 
            var options = {
                title: titulo,
                is3D: true,
                height: 300,
                width: 450
            };
 
            var controle = new google.visualization.PieChart(
                document.getElementById(idControle));
            controle.draw(data, options);
        }
 
    </script>
 
    <script type="text/javascript">
 
        jQuery.support.cors = true;
 
        google.load("visualization", "1",
            { packages: ["corechart"] });
 
 
        google.setOnLoadCallback(gerarVisualizacaoGraficoExportacoes);
 
        function gerarVisualizacaoGraficoExportacoes() {
            gerarVisualizacao('EXPORTACOES',
                'divExportacoesContinentes');
        }
 
 
        google.setOnLoadCallback(gerarVisualizacaoGraficoImportacoes);
 
        function gerarVisualizacaoGraficoImportacoes() {
            gerarVisualizacao('IMPORTACOES',
                'divImportacoesContinentes');
        }
 
    </script>
 
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" 
             runat="server">
    <h4>Balanço por Continente – 
        <%: Request.QueryString["ano"] %></h4>
    <br />
    <div>
        <div id="divExportacoesContinentes"
             style="float: left;">
        </div>
        <div id="divImportacoesContinentes"
             style="margin-left: 450px;">
        </div>
    </div>
    <br />
    <asp:HyperLink ID="lnkVoltar"
        NavigateUrl="~/Default.aspx"
        runat="server">Voltar</asp:HyperLink>
</asp:Content> 
Listagem 7. Página BalancoContinentes.aspx

Uma segunda página de nome BalancoPaises.aspx deverá ser gerada, a fim de possibilitar a visualização de exportações/importações por país. Na Listagem 8 está o código que define este formulário.

As instruções em JavaScript/jQuery presentes no formulário BalancoPaises.aspx são bastante similares àquelas que constam na página BalancoContinentes.aspx. A única diferença mais significativa está na criação de instâncias dos tipos google.visualization.IntensityMap e google.visualization.Table, as quais permitirão a geração de um gráfico de intensidade por país e de uma tabela para a exibição de informações (a partir dos dados retornados pela operação ObterBalancoPaises do serviço WCF).


<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master"
    AutoEventWireup="true" CodeBehind="BalancoPaises.aspx.cs"
    Inherits="TesteWCFJSON.ConsumidorWebForms.BalancoPaises" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent"
             runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent"
             runat="server">
    <script type="text/javascript"
            src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">

        function gerarVisualizacao(tipo, idControle, options) {

            var ano = '<%: Request.QueryString["ano"] %>';

            var data = new google.visualization.DataTable();
            data.addColumn('string', 'País');
            data.addColumn('number', 'Exportações (em milhões R$)');
            data.addColumn('number', 'Importações (em milhões R$)');

            var urlBalanco =
                'http://localhost:55486/' +
                'BalancoComercialWS.svc/paises/' +
                ano;

            var retornoWS;
            $.ajax(
            {
                type: 'GET',
                url: urlBalanco,
                dataType: 'json',
                crossDomain: true,
                async: false,
                success: function (data) {
                    retornoWS = data;
                }
            });

            var dadosBalanco = [];
            $.each(retornoWS, function (i, balanco) {
                if (tipo == 'IntensityMap') {
                    dadosBalanco.push([
                        balanco.Sigla.toString(),
                        parseFloat(balanco.ValorExportado),
                        parseFloat(balanco.ValorImportado)]);
                }
                else {
                    dadosBalanco.push([
                        balanco.Pais.toString(),
                        parseFloat(balanco.ValorExportado),
                        parseFloat(balanco.ValorImportado)]);
                }
            });
            data.addRows(dadosBalanco);

            var formatter = new google.visualization.NumberFormat(
                {
                    decimalSymbol: ',',
                    groupingSymbol: '.',
                    fractionDigits: 1
                });
            formatter.format(data, 1);
            formatter.format(data, 2);

            var controle;
            if (tipo == 'IntensityMap') {
                controle = new google.visualization.IntensityMap(
                    document.getElementById(idControle));
            }
            else if (tipo == 'Table') {
                controle = new google.visualization.Table(
                    document.getElementById(idControle));
            }
            controle.draw(data, options);
        }

    </script>

    <script type="text/javascript">

        jQuery.support.cors = true;

        google.load("visualization", "1",
            { packages: ["intensitymap"] });
        google.setOnLoadCallback(gerarVisualizacaoMapaIntensidade);

        function gerarVisualizacaoMapaIntensidade() {
            gerarVisualizacao('IntensityMap', 'divMapaPaises', {});
        }

        google.load('visualization', '1', { packages: ['table'] });
        google.setOnLoadCallback(gerarVisualizacaoTabelaPaises);

        function gerarVisualizacaoTabelaPaises() {
            gerarVisualizacao('Table', 'divTabelaPaises',
                {
                    width: 600
                });
        }

    </script>

</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent"
             runat="server">
    <h4>Balanço por País - <%: Request.QueryString["ano"] %></h4>
    <br />
    <div id="divMapaPaises">
    </div>
    <br /><br />
    <div id="divTabelaPaises">
    </div>
    <br />
    <asp:HyperLink ID="lnkVoltar"
       NavigateUrl="~/Default.aspx"
       runat="server">Voltar</asp:HyperLink>
</asp:Content>
Listagem 8. Página BalancoPaises.aspx

Por fim, será necessário alterar o código da página Default.aspx, incluindo links que permitam a visualização dos gráficos existentes nas páginas BalancoContinentes.aspx e BalancoPaises.aspx (Listagem 9).


<%@ Page Title="Home Page" Language="C#"
    MasterPageFile="~/Site.Master"
    AutoEventWireup="true" CodeBehind="Default.aspx.cs"
    Inherits="TesteWCFJSON.ConsumidorWebForms._Default" %>

<asp:Content runat="server" ID="FeaturedContent"
             ContentPlaceHolderID="FeaturedContent">
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>Consulta - Balanço Comercial – 
                    Indústrias ACME</h1>
            </hgroup>
        </div>
    </section>
</asp:Content>
<asp:Content runat="server" ID="BodyContent"
             ContentPlaceHolderID="MainContent">
    <ol class="round">
        <li class="one">
            <h5>Balanço Comercial por Continente</h5>
            <p>
                <asp:HyperLink ID="lnkBalancoContinentes2011"
                    runat="server"
                    NavigateUrl="~/BalancoContinentes.aspx?ano=2011">
                2011
                </asp:HyperLink>
            </p>
            <p>
                <asp:HyperLink ID="lnkBalancoContinentes2012"
                    runat="server"
                    NavigateUrl="~/BalancoContinentes.aspx?ano=2012">
                2012
                </asp:HyperLink>
            </p>
        </li>
        <li class="two">
            <h5>Balanço Comercial por País</h5>
            <p>
                <asp:HyperLink ID="lnkBalancoPaises2011"
                    runat="server"
                    NavigateUrl="~/BalancoPaises.aspx?ano=2011">
                2011
                </asp:HyperLink>
            </p>
            <p>
                <asp:HyperLink ID="lnkBalancoPaises2012"
                    runat="server"
                    NavigateUrl="~/BalancoPaises.aspx?ano=2012">
                2012
                </asp:HyperLink>
            </p>
        </li>
    </ol>
</asp:Content>
Listagem 9. Página Default.aspx

Na Figura 5 é apresentada a tela inicial que aparecerá ao se executar a aplicação TesteWCFJSON.ConsumidorWebForms.

Executando a aplicação TesteWCFJSON.ConsumidorWebForms
Figura 5. Executando a aplicação TesteWCFJSON.ConsumidorWebForms

Selecionando o ano de 2012 para a opção “Balanço Comercial por Continente”, serão exibidos os gráficos de exportações e importações para este período (Figura 6).

Exportações e importações por continente no ano de 2012
Figura 6. Exportações e importações por continente no ano de 2012

Já na Figura 7 está a tela que será apresentada ao se acionar o ano de 2012, dentro da opção “Balanço Comercial por País” (a qual se encontra na página inicial do site de testes).

Exportações e importações por país no ano de 2012
Figura 7. Exportações e importações por país no ano de 2012

Conclusão

Conforme demonstrado ao longo deste artigo, a implementação de serviços WCF que serão consumidos via código JavaScript difere muito pouco da abordagem tradicional, ou seja, aquela em que Web Services são construídos segundo o protocolo SOAP (o qual se baseia na troca de mensagens empregando o padrão XML).

As únicas mudanças significativas estão na forma como o serviço será configurado, de maneira a tornar suas operações acessíveis a aplicações que dependam do mesmo. O fato de soluções WCF manipularem dados em formatos como JSON e WCF permite, inclusive, que uma ampla gama de plugins e componentes gerados a partir da biblioteca jQuery façam uso sem maiores dificuldades de funcionalidades disponibilizadas por Web Services deste tipo.

Espero que o conteúdo aqui apresentado possa ser útil no seu dia-a-dia. Até uma próxima oportunidade!