Desde o seu surgimento em 2006, a adoção de jQuery em aplicações Web vem crescendo vertiginosamente. Nos dias atuais, o uso desta biblioteca JavaScript em sites é quase uma unanimidade. Concebida com o intuito de simplificar a manipulação de elementos HTML em páginas da Web (evitando assim a codificação de instruções muito extensas), jQuery é uma alternativa que se destaca ainda pelo esforço em garantir um grau mínimo de compatibilidade com os principais browsers do mercado.

Outro aspecto marcante da biblioteca jQuery está na utilização de recursos de AJAX (sigla do inglês “Asynchronous JavaScript and XML”) na implementação de eventos, animações e outros tipos de funcionalidades dinâmicas. Por este motivo, inúmeros plug-ins, controles e frameworks voltados à construção de interfaces gráficas têm sido construídos a partir de jQuery. Há inclusive uma versão desta biblioteca (o framework jQuery Mobile) concebida para o desenvolvimento em dispositivos móveis.

Devido à forte ênfase dada à interoperabilidade, soluções em jQuery costumam utilizar formatos abertos e de grande aceitação na manipulação de informações. Embora o uso de XML seja possível, o padrão //www.devmedia.com.br/curso/jquery-mobile/361 (abreviação do inglês “JavaScript Object Notation”) corresponde à opção mais frequente para o tratamento de dados dentro de jQuery. A fonte de tais informações pode tanto ser alguma funcionalidade de uma aplicação que utiliza esta biblioteca, quanto um serviço acessado de maneira remota via JavaScript.

No caso específico da plataforma .NET, uma das possibilidades para a construção de Web Services seria a utilização do framework ASP.NET Web API. Tomando por base a arquitetura REST, esta tecnologia permite a implementação de serviços que combinam o uso de formatos como JSON e XML à manipulação de requisições HTTP.

O objetivo deste artigo é demonstrar, partindo de uma aplicação de exemplo, como serviços //www.devmedia.com.br/cursos/net podem ser consumidos a partir de código escrito em jQuery/JavaScript. Isto será feito através da construção de uma aplicação ASP.NET MVC que emprega a biblioteca Google Chart Tools, algo que será descrito nas próximas seções.

Exemplo de consumo de um serviço Web API via jQuery

A solução descrita neste artigo foi construída empregando os seguintes recursos:

  • O Microsoft Visual Studio Professional 2013 Update 3 como IDE de desenvolvimento;
  • O .NET Framework 4.5.1;
  • O framework ASP.NET Web API 2.2 no serviço que será consumido via jQuery;
  • O framework ASP.NET MVC 5.2.2 como base para a criação das interfaces gráficas de testes;
  • A biblioteca de scripts jQuery;
  • A biblioteca Google Chart Tools, a qual é formada um conjunto de plugins jQuery empregados na geração de gráficos.

A biblioteca Google Chart Tools corresponde a um conjunto de recursos baseados em jQuery/JavaScript e oferecidos gratuitamente pela Google, tendo por objetivo fornecer meios para a criação de gráficos dos mais variados tipos de uma maneira simples e flexível. Diversas opções permitem customizar a aparência de um gráfico, além deste framework JavaScript possuir compatibilidade com os principais browsers e plataformas do mercado (Internet Explorer, Chrome, Firefox, Android, iOS). Gráficos montados a partir desta biblioteca podem tanto contar com a representação tradicional em que as informações são dispostas em eixos, quanto terem seus dados organizados na forma de tabelas (permitindo inclusive a ordenação ascendente/descendente das respectivas colunas).

O processo de renderização das imagens para a geração de gráficos emprega funcionalidades oferecidas pelas tecnologias HTML5 e SVG (sigla do inglês “Scalable Vector Graphics”), as quais procuram assegurar portabilidade na utilização de elementos visuais pelos mais diversos browsers e plataformas.

O exemplo em questão é uma adaptação de um projeto Web Forms.Quanto aos Web Services que servirão de base para testes, a aplicação correspondente (TesteWebAPI.Services) também já foi implementada anteriormente em outro post que escrevi para o portal DevMedia.

Importante lembrar ainda que o projeto TesteWebAPI.Services faz parte de uma Solution chamada TesteWebAPI. Esta aplicação conta com dois Web Services: cada uma destas estruturas retornará o balanço comercial (exportações e importações) por países ou continentes dentro de ano de referência, levando em conta para isto as operações de uma companhia hipotética.

Neste novo artigo será adicionada ainda à solução uma aplicação MVC chamada TesteWebAPI.MVCxjQuery. Por meio deste novo site acontecerão testes, utilizando como base os anos de 2012 e 2013.

Criando o projeto de TesteWebAPI.MVCxjQuery

O projeto TesteWebAPI.MVCxjQuery será do tipo “ASP.NET Web Application” (conforme indicado na Figura 1), devendo ser criado a partir da seleção do template “MVC” sem a utilização de mecanismos de autenticação (Figura 2). A ideia principal com esta aplicação é demonstrar de que forma serviços RESTful criados em Web API são acessados a partir de código escrito em jQuery.

Criando uma ASP.NET
Web Application no Visual Studio 2013

Figura 1. Criando uma ASP.NET Web Application no Visual Studio 2013

Selecionando o
template para a criação de uma Web Application

Figura 2. Selecionando o template para a criação de uma Web Application

Na Tabela 1 estão listadas as URLs de acesso aos Web Services do projeto TesteWebAPI.Services. O valor “60913” indica um número de porta que estava disponível no momento em que o projeto foi gerado; já “{ano}” se refere ao período(ano) a ser considerado para a obtenção dos dados com o balanço comercial.

Web Service

Padrão da URL

Balanço Comercial por País

http://localhost:60913/balanco/paises/{ano}

Balanço Comercial por Continente

http://localhost:60913/balanco/continentes/{ano}

Tabela 1. URLs de acesso aos serviços do projeto TesteWebAPI.Services

O próximo passo agora será modificar o arquivo Web.config do projeto TesteWebAPI.MVCxjQuery (Listagem 1), de forma que que constem na seção appSettings do mesmo os endereços para consumo dos serviços Web API.

Listagem 1. Arquivo Web.config do projeto TesteWebAPI.MVCxjQuery


  <?xml version="1.0" encoding="utf-8"?>
  <configuration>
    <appSettings>
   
      ...
   
      <add key="UrlBalancoPaises"
           value="http://localhost:60913/balanco/paises/" />
      <add key="UrlBalancoContinentes"
           value="http://localhost:60913/balanco/continentes/" />
    </appSettings>
   
    ...
   
  </configuration>

Um novo Controller chamado “BalancoController” deverá ser adicionado ao projeto TesteWebAPI.MVCxjQuery. Na Listagem 2 está o código que define essa estrutura.

Analisando o Controller BalancoController é possível observar:

  • A existência de duas Actions (Paises e Continentes), as quais recebem como parâmetros um ano de referência. Estes dois métodos servirão de base para a geração das Views em que constarão os balanços comerciais por país e continente;
  • As duas Actions citadas utilizam o objeto ViewBag. Estão sendo associadas a esta propriedade o ano de referência, além da URL em que se encontra o serviço Web API responsável por retornar informações do balanço comercial.

Listagem 2. Controller BalancoController


  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using System.Web.Mvc;
  using System.Configuration;
   
  namespace TesteWebAPI.MVCxjQuery.Controllers
  {
      public class BalancoController : Controller
      {
          public ActionResult Paises(int ano)
          {
              ViewBag.Ano = ano;
              ViewBag.UrlBalancoPaises =
                  ConfigurationManager.AppSettings["UrlBalancoPaises"];
              return View();
          }
   
          public ActionResult Continentes(int ano)
          {
              ViewBag.Ano = ano;
              ViewBag.UrlBalancoContinentes =
                  ConfigurationManager.AppSettings["UrlBalancoContinentes"];
              return View();
          }
      }
  }

A primeira das Views a ser criada permitirá a visualização dos dados de importações e exportações por país. Para gerar o arquivo correspondente clicar com o botão direito do mouse sobre a Action Paises, selecionando no menu de atalho apresentado a opção “Add View...”. Com a janela “Add View” ativa (Figura 3), certificar-se de que o campo “Template” está preenchido com o valor “Empty (without model)” e que a opção “Create as partial view” não tenha sido marcada.

Criação da View
Paises.cshtml

Figura 3. Criação da View Paises.cshtml

Na Listagem 3 está a implementação da View Paises.cshtml, a qual tornará possível a visualização de gráficos que consideram os totais de importação e exportação por país.

A geração de gráficos com o Google Chart Tools através de dados obtidos de um serviço Web API engloba as seguintes ações:

  • O preenchimento da propriedade cors do objeto support de jQuery com o valor “true”, possibilitando com isto o envio de requisições ao Web Service de testes;
  • A invocação do método load a partir do objeto google, de forma a carregar as bibliotecas para geração de gráficos;
  • A criação de funções que serão acionadas a partir do método setOnLoadCallback do objeto google (gerarVisualizacaoMapaIntensidade e gerarVisualizacaoTabelaPaises).

Os métodos gerarVisualizacaoMapaIntensidade e gerarVisualizacaoTabelaPaises estão acessando a função gerarVisualizacao. Já esta última função recebe como parâmetros o tipo de gráfico a ser gerado, assim como o id do controle HTML em que será renderizado o gráfico (normalmente uma div) e um conjunto de opções que determinam a aparência deste.

Quanto à implementação do método gerarVisualizacao, é possível observar:

  • A 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 por meio do método addColumn (que recebe como parâmetros o tipo e a descrição do campo);
  • Uma chamada via instrução jQuery ao método do serviço Web API que fornecerá o balanço por país num determinado ano. Utilizando o comando $.ajax e uma URL em que consta o ano de referência, a operação Get definida no serviço Web API de país é acionada (de forma síncrona, ou seja, aguardando a execução completa do método, algo indicado por meio do parâmetro async). O resultado deste procedimento será um conjunto de informações no formato JSON;
  • Os dados retornados pelo Web Service são convertidos então para um array, fazendo uso para isto da instrução $.each (que permite navegar a cada objeto JSON obtido, convertendo as informações que compõem o mesmo para a matriz no formato esperado pelo Data Table);
  • Um objeto do tipo google.visualization.NumberFormat é gerado, com intuito de se proceder com a formatação de valores monetários;
  • Como último passo o gráfico será gerado, de acordo com o tipo informado inicialmente ao se acessar a função gerarVisualizacao. Tomando como exemplo a View Paises.cshtml, a instância que representa o gráfico poderá ser de dois tipos, google.visualization.IntensityMap (um mapa de intensidade) ou google.visualization.Table (uma tabela). O resultado produzido por um objeto do Google Chart Tools será renderizado no elemento HTML indicado ao se acionar a função gerarVisualizacao.

Listagem 3. View Paises.cshtml (Controller BalancoController)


  <h2>Balanço por País - @ViewBag.Ano</h2>
  <br />
  <div id="divMapaPaises">
  </div>
  <br /><br />
  <div id="divTabelaPaises">
  </div>
  <br />
   
   
  @section Scripts {
      @Scripts.Render("http://www.google.com/jsapi")
   
      <script type="text/javascript">
   
          function gerarVisualizacao(tipo, idControle, options) {
   
              var ano = '@ViewBag.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 = '@ViewBag.UrlBalancoPaises' + 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>
  }

Uma segunda View associada à Action Continentes deverá ser criada, a fim de possibilitar a visualização de exportações/importações por continentes. Na Listagem 4 está o código que define esta View.

As instruções em JavaScript/jQuery que constam no arquivo Continentes.cshtml são bastante similares àquelas existentes em Paises.cshtml. A única distinção mais notável está no uso do tipo google.visualization.PieChart: partindo desta estrutura serão gerados dois gráficos do tipo pizza, um para exportações e outro relativo a importações por continentes.

Listagem 4. View Continentes.cshtml (Controller BalancoController)


   
  <h2>Balanço por Continente - @ViewBag.Ano</h2>
  <br />
  <div>
      <div id="divExportacoesContinentes" style="float: left;">
      </div>
      <div id="divImportacoesContinentes" style="margin-left: 450px;">
      </div>
  </div>
  <br />
   
  @section Scripts {
      @Scripts.Render("http://www.google.com/jsapi")
   
      <script type="text/javascript">
   
          function gerarVisualizacao(tipo, idControle) {
   
              var ano = '@ViewBag.Ano';
   
              var data = new google.visualization.DataTable();
              data.addColumn('string', 'Continente');
              data.addColumn('number', 'Valor');
   
              var urlBalanco = '@ViewBag.UrlBalancoContinentes' + 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
array3
nbsp;(milhões)',
decimalSymbol:',',
groupingSymbol:'.',
fractionDigits:1
});
formatter.format(data,1);

vartitulo;
if(tipo=='EXPORTACOES')
titulo='ExportaçõesporContinente-'+ano;
else
titulo='ImportaçõesporContinente-'+ano;

varoptions={
title:titulo,
is3D:true,
height:300,
width:450
};

varcontrole=newgoogle.visualization.PieChart(
document.getElementById(idControle));
controle.draw(data,options);
}

</script>

<scripttype="text/javascript">

jQuery.support.cors=true;

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


google.setOnLoadCallback(gerarVisualizacaoGraficoExportacoes);

functiongerarVisualizacaoGraficoExportacoes(){
gerarVisualizacao('EXPORTACOES','divExportacoesContinentes');
}


google.setOnLoadCallback(gerarVisualizacaoGraficoImportacoes);

functiongerarVisualizacaoGraficoImportacoes(){
gerarVisualizacao('IMPORTACOES','divImportacoesContinentes');
}
</script>
}

Por fim, a View Index do Controller HomeController precisará ser alterada. Os ajustes indicados na Listagem 5 referem-se a links que possibilitarão a consulta dos balanços comerciais por países e continentes, considerando para isto os anos de 2012 e 2013. A geração de tais links é possível graças a chamadas ao método ActionLink, o qual é disponibilizado pelo objeto Html para uso em Views do ASP.NET MVC.

Listagem 5. View Index.cshtml (Controller HomeController)


  <div class="jumbotron">
      <h1>Integração ASP.NET Web API x jQuery</h1>
      <p><a href="http://www.asp.net/web-api"
            class="btn btn-primary btn-large">Saiba mais »</a></p>
  </div>
   
   
  <div style="padding-left: 50px;">
      <div>
          <h2>
              Balanço Comercial por País
          </h2>
          <h3>
              @Html.ActionLink("2012", "Paises", "Balanco",
                  new { ano = 2012 }, new { })
                 
              @Html.ActionLink("2013", "Paises", "Balanco",
                  new { ano = 2013 }, new { })
          </h3>
      </div>
      <div>
          <h2>
              Balanço Comercial por Continente
          </h2>
          <h3>
              @Html.ActionLink("2012", "Continentes", "Balanco",
                  new { ano = 2012 }, new { })
                 
              @Html.ActionLink("2013", "Continentes", "Balanco",
                  new { ano = 2013 }, new { })
          </h3>
      </div>
  </div>

OBSERVAÇÃO: Por questões de simplificação, o código referente às outras Views da aplicação de testes foi omitido, já que no mesmo não constam instruções complexas que justifiquem uma discussão mais aprofundada. Os arquivos correspondentes a essas Views podem ser obtidos através do download da solução aqui implementada, bastando acessar o link para download do material deste artigo. Este mesmo procedimento também foi adotado com a classe HomeController.

Ajustes nas configurações do projeto TesteWebAPI.Services

Para que a utilização dos controles do Google Chart Tools aconteça sem problemas a partir do Google Chrome, o arquivo Web.config do projeto TesteWebAPI.Services também deverá ser alterado com a inclusão de algumas configurações na seção “system.webServer” (conforme especificado na Listagem 6).

Listagem 6: Arquivo Web.config (projeto TesteWebAPI.Services)


<?xml version="1.0" encoding="utf-8"?>
<configuration>

  ...

  <system.webServer>

    ...

    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Executando a aplicação de testes

Na Figura 4 está a tela apresentada inicialmente ao se executar a aplicação TesteWebAPI.MVCxjQuery.

Tela inicial da
aplicação TesteWebAPI.MVCxjQuery

Figura 4. Tela inicial da aplicação TesteWebAPI.MVCxjQuery

Selecionando o ano de 2012 para a opção “Balanço Comercial por País”, serão exibidas duas diferentes visões de exportações e importações por país (Figura 5).

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

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

Já na Figura 6 está a tela que será apresentada ao se acionar o ano de 2013, a partir da opção “Balanço Comercial por Continente” (na página inicial do site de testes).

Exportações e
importações por continente no ano de 2013

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

A intenção deste artigo foi demonstrar como serviços RESTful podem ser consumidos via código JavaScript/jQuery. Embora tenha sido empregado o framework ASP.NET Web API na implementação dos Web Services que foram consumidos, os procedimentos aqui descritos também são válidos no acesso a informações providas por aplicações construídas em WCF (Windows Communication Foundation) ou, até mesmo, em outras plataformas (como Java, por exemplo).

Espero que o conteúdo aqui apresentado possa ser útil no seu dia a dia.

Até uma próxima oportunidade!

Links

ASP.NET Web API -http://www.asp.net/web-api

Google Chart Tools -https://developers.google.com/chart/