Noções básicas sobre os serviços Web do AJAX ASP.NET
por Scott Cate
Os Web Services são parte integrante da estrutura .NET que fornecem uma solução multiplataforma para troca de dados entre sistemas distribuídos. Embora os serviços da Web sejam normalmente usados para permitir que diferentes sistemas operacionais, modelos de objeto e linguagens de programação enviem e recebam dados, eles também podem ser usados para injetar dados dinamicamente em uma página AJAX do ASP.NET ou enviar dados de uma página para um sistema de back-end. Tudo isso pode ser feito sem recorrer a operações de postback.
Chamando serviços Web com ASP.NET AJAX
Dan Wahlin
Os Web Services são parte integrante da estrutura .NET que fornecem uma solução multiplataforma para troca de dados entre sistemas distribuídos. Embora os serviços da Web sejam normalmente usados para permitir que diferentes sistemas operacionais, modelos de objeto e linguagens de programação enviem e recebam dados, eles também podem ser usados para injetar dados dinamicamente em uma página AJAX do ASP.NET ou enviar dados de uma página para um sistema de back-end. Tudo isso pode ser feito sem recorrer a operações de postback.
Embora o controle UpdatePanel do ASP.NET AJAX forneça uma maneira simples de habilitar o AJAX para qualquer página ASP.NET, pode haver momentos em que você precise acessar dinamicamente os dados no servidor sem usar um UpdatePanel. Neste artigo, você verá como fazer isso criando e consumindo serviços Web em ASP.NET páginas AJAX.
Este artigo se concentrará na funcionalidade disponível no núcleo ASP.NET extensões AJAX, bem como em um controle habilitado para serviço Web no ASP.NET AJAX Toolkit chamado AutoCompleteExtender. Os tópicos abordados incluem a definição de serviços da Web habilitados para AJAX, a criação de proxies de cliente e a chamada de serviços da Web com JavaScript. Você também verá como as chamadas de serviço Web podem ser feitas diretamente para ASP.NET métodos de página.
Configuração de serviços da Web
Quando um novo projeto de Site é criado com o Visual Studio 2008, o arquivo web.config tem várias novas adições que podem não ser familiares aos usuários de versões anteriores do Visual Studio. Algumas dessas modificações mapeiam o prefixo "asp" para ASP.NET controles AJAX para que possam ser usados em páginas, enquanto outras definem HttpHandlers e HttpModules necessários. A Listagem 1 mostra as modificações feitas no elemento em web.config que afetam as <httpHandlers>
chamadas de serviço da Web. O HttpHandler padrão usado para processar chamadas .asmx é removido e substituído por uma classe ScriptHandlerFactory localizada no assembly System.Web.Extensions.dll. System.Web.Extensions.dll contém todas as funcionalidades principais usadas pelo ASP.NET AJAX.
Listagem 1. ASP.NET Configuração do manipulador de serviços Web AJAX
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false"
type="System.Web.Script.Services.ScriptHandlerFactory,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
</httpHandlers>
Essa substituição de HttpHandler é feita para permitir que chamadas JSON (JavaScript Object Notation) sejam feitas de páginas AJAX ASP.NET para Serviços Web .NET usando um proxy de Serviço Web JavaScript. ASP.NET AJAX envia mensagens JSON para serviços da Web em oposição às chamadas padrão do Simple Object Access Protocol (SOAP) normalmente associadas aos serviços da Web. Isso resulta em mensagens de solicitação e resposta menores em geral. Ele também permite um processamento de dados mais eficiente do lado do cliente, pois a biblioteca JavaScript do ASP.NET AJAX é otimizada para trabalhar com objetos JSON. A Listagem 2 e a Listagem 3 mostram exemplos de mensagens de solicitação e resposta do Web Service serializadas para o formato JSON. A mensagem de solicitação mostrada na Listagem 2 passa um parâmetro country com um valor de "Belgium", enquanto a mensagem de resposta na Listagem 3 passa uma matriz de objetos Customer e suas propriedades associadas.
Listagem 2. Mensagem de solicitação de serviço Web serializada para JSON
{"country":"Belgium"}
> [! NOTE] o nome da operação é definido como parte da URL para o serviço Web; além disso, as mensagens de solicitação nem sempre são enviadas via JSON. Os serviços Web podem utilizar o atributo ScriptMethod com o parâmetro UseHttpGet definido como true, o que faz com que os parâmetros sejam passados por meio de parâmetros de cadeia de caracteres de consulta.
Listagem 3. Mensagem de resposta do serviço Web serializada para JSON
[{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Maison
Dewey","CustomerID":"MAISD","ContactName":"Catherine
Dewey"},{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Suprêmes
délices","CustomerID":"SUPRD","ContactName":"Pascale
Cartrain"}]
Na próxima seção, você verá como criar serviços Web capazes de lidar com mensagens de solicitação JSON e responder com tipos simples e complexos.
Criando serviços Web habilitados para AJAX
A estrutura do ASP.NET AJAX fornece várias maneiras diferentes de chamar serviços da Web. Você pode usar o controle AutoCompleteExtender (disponível no ASP.NET AJAX Toolkit) ou JavaScript. No entanto, antes de chamar um serviço, você precisa habilitá-lo para AJAX para que ele possa ser chamado pelo código de script do cliente.
Independentemente de você ser novo ou não no ASP.NET Web Services, você achará simples criar e habilitar serviços para AJAX. O .NET Framework oferece suporte à criação de ASP.NET Web Services desde seu lançamento inicial em 2002 e as extensões do ASP.NET AJAX fornecem funcionalidade AJAX adicional que se baseia no conjunto de recursos padrão do .NET Framework. O Visual Studio .NET 2008 Beta 2 tem suporte interno para a criação de arquivos de serviço Web .asmx e deriva automaticamente o código associado ao lado de classes da classe System.Web.Services.WebService. Ao adicionar métodos à classe, você deve aplicar o atributo WebMethod para que eles sejam chamados pelos consumidores do serviço Web.
A Listagem 4 mostra um exemplo de aplicação do atributo WebMethod a um método chamado GetCustomersByCountry().
Listagem 4. Usando o atributo WebMethod em um serviço Web
[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
O método GetCustomersByCountry() aceita um parâmetro country e retorna uma matriz de objeto Customer. O valor do país passado para o método é encaminhado para uma classe de camada de negócios que, por sua vez, chama uma classe de camada de dados para recuperar os dados do banco de dados, preencher as propriedades do objeto Customer com dados e retornar a matriz.
Usando o atributo ScriptService
Embora a adição do atributo WebMethod permita que o método GetCustomersByCountry() seja chamado por clientes que enviam mensagens SOAP padrão para o serviço Web, ele não permite que chamadas JSON sejam feitas a partir de aplicativos AJAX prontos para uso ASP.NET. Para permitir que chamadas JSON sejam feitas ScriptService
, você precisa aplicar o atributo da extensão AJAX ASP.NET à classe Web Service. Isso permite que um serviço Web envie mensagens de resposta formatadas usando JSON e permite que o script do lado do cliente chame um serviço enviando mensagens JSON.
A Listagem 5 mostra um exemplo de aplicação do atributo ScriptService a uma classe de serviço da Web chamada CustomersService.
Listagem 5. Usando o atributo ScriptService para habilitar um serviço Web para AJAX
[System.Web.Script.Services.ScriptService]
[WebService(Namespace = "http://xmlforasp.net")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CustomersService : System.Web.Services.WebService
{
[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
}
O atributo ScriptService atua como um marcador que indica que ele pode ser chamado do código de script AJAX. Na verdade, ele não lida com nenhuma das tarefas de serialização ou desserialização JSON que ocorrem nos bastidores. O ScriptHandlerFactory (configurado em web.config) e outras classes relacionadas fazem a maior parte do processamento JSON.
Usando o atributo ScriptMethod
O atributo ScriptService é o único atributo AJAX ASP.NET que precisa ser definido em um serviço Web .NET para que ele seja usado por páginas AJAX ASP.NET. No entanto, outro atributo chamado ScriptMethod também pode ser aplicado diretamente aos Métodos Web em um serviço. ScriptMethod define três propriedades, incluindo UseHttpGet
, ResponseFormat
e XmlSerializeString
. Alterar os valores dessas propriedades pode ser útil nos casos em que o tipo de solicitação aceito por um método da Web precisa ser alterado para GET, quando um método da Web precisa retornar dados XML brutos na forma de um XmlDocument
objeto ou XmlElement
ou quando os dados retornados de um serviço devem sempre ser serializados como XML em vez de JSON.
A propriedade UseHttpGet pode ser usada quando um método Web deve aceitar solicitações GET em vez de solicitações POST. As solicitações são enviadas usando uma URL com parâmetros de entrada do Método da Web convertidos em parâmetros QueryString. A propriedade UseHttpGet é padronizada como false e só deve ser definida como true
quando as operações são conhecidas como seguras e quando dados confidenciais não são passados para um serviço Web. A Listagem 6 mostra um exemplo de uso do atributo ScriptMethod com a propriedade UseHttpGet.
Listagem 6. Usando o atributo ScriptMethod com a propriedade UseHttpGet.
[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
return input;
}
Um exemplo dos cabeçalhos enviados quando o método da Web HttpGetEcho mostrado na Listagem 6 é chamado é mostrado a seguir:
GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1
Além de permitir que os Métodos da Web aceitem solicitações HTTP GET, o atributo ScriptMethod também pode ser usado quando as respostas XML precisam ser retornadas de um serviço em vez de JSON. Por exemplo, um serviço Web pode recuperar um feed RSS de um site remoto e retorná-lo como um objeto XmlDocument ou XmlElement. O processamento dos dados XML pode ocorrer no cliente.
A Listagem 7 mostra um exemplo de uso da propriedade ResponseFormat para especificar que os dados XML devem ser retornados de um método da Web.
Listagem 7. Usando o atributo ScriptMethod com a propriedade ResponseFormat.
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
XmlDocument doc = new XmlDocument();
doc.Load(url);
return doc.DocumentElement;
}
A propriedade ResponseFormat também pode ser usada junto com a propriedade XmlSerializeString. A propriedade XmlSerializeString tem um valor padrão de false, o que significa que todos os tipos de retorno, exceto as cadeias de caracteres retornadas de um método da Web, são serializados como XML quando a ResponseFormat
propriedade é definida como ResponseFormat.Xml
. Quando XmlSerializeString
é definido como true
, todos os tipos retornados de um método da Web são serializados como XML, incluindo tipos de cadeia de caracteres. Se a propriedade ResponseFormat tiver um valor da ResponseFormat.Json
propriedade XmlSerializeString será ignorada.
A Listagem 8 mostra um exemplo de uso da propriedade XmlSerializeString para forçar as cadeias de caracteres a serem serializadas como XML.
Listagem 8. Usando o atributo ScriptMethod com a propriedade XmlSerializeString
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
return input;
}
O valor retornado da chamada do método da Web GetXmlString mostrado na Listagem 8 é mostrado a seguir:
<?xml version="1.0"?>
<string>Test</string>
Embora o formato JSON padrão minimize o tamanho geral das mensagens de solicitação e resposta e seja mais prontamente consumido por clientes AJAX ASP.NET entre navegadores, as propriedades ResponseFormat e XmlSerializeString podem ser utilizadas quando aplicativos cliente, como o Internet Explorer 5 ou superior, esperam que os dados XML sejam retornados de um método Web.
Trabalhando com tipos complexos
A Listagem 5 mostrou um exemplo de retorno de um tipo complexo chamado Customer de um serviço da Web. A classe Customer define vários tipos simples diferentes internamente como propriedades como FirstName e LastName. Os tipos complexos usados como parâmetro de entrada ou tipo de retorno em um método da Web habilitado para AJAX são serializados automaticamente em JSON antes de serem enviados para o lado do cliente. No entanto, os tipos complexos aninhados (aqueles definidos internamente em outro tipo) não são disponibilizados para o cliente como objetos autônomos por padrão.
Nos casos em que um tipo complexo aninhado usado por um serviço Web também deve ser usado em uma página de cliente, o atributo GenerateScriptType do ASP.NET AJAX pode ser adicionado ao serviço Web. Por exemplo, a classe CustomerDetails mostrada na Listagem 9 contém as propriedades Address e Gender que representam tipos complexos aninhados.
Listagem 9. A classe CustomerDetails mostrada aqui contém dois tipos complexos aninhados.
public class CustomerDetails : Customer
{
public CustomerDetails()
{
}
Address _Address;
Gender _Gender = Gender.Unknown;
public Address Address
{
get { return _Address; }
set { _Address = value; }
}
public Gender Gender
{
get { return _Gender; }
set { _Gender = value; }
}
}
Os objetos Address e Gender definidos na classe CustomerDetails mostrada na Listagem 9 não serão disponibilizados automaticamente para uso no lado do cliente via JavaScript, pois são tipos aninhados (Address é uma classe e Gender é uma enumeração). Em situações em que um tipo aninhado usado em um serviço da Web deve estar disponível no lado do cliente, o atributo GenerateScriptType mencionado anteriormente pode ser usado (consulte a Listagem 10). Esse atributo pode ser adicionado várias vezes nos casos em que diferentes tipos complexos aninhados são retornados de um serviço. Ele pode ser aplicado diretamente à classe Web Service ou acima de Web Methods específicos.
Listagem 10. Usando o atributo GenerateScriptService para definir tipos aninhados que devem estar disponíveis para o cliente.
[System.Web.Script.Services.ScriptService]
[System.Web.Script.Services.GenerateScriptType(typeof(Address))]
[System.Web.Script.Services.GenerateScriptType(typeof(Gender))]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class NestedComplexTypeService : System.Web.Services.WebService
{
//Web Methods
}
Ao aplicar o GenerateScriptType
atributo ao Serviço Web, os tipos Address e Gender serão disponibilizados automaticamente para uso pelo código JavaScript AJAX ASP.NET do lado do cliente. Um exemplo do JavaScript que é gerado e enviado automaticamente ao cliente incluindo o atributo GenerateScriptType em um serviço da Web é mostrado na Listagem 11. Você verá como usar tipos complexos aninhados posteriormente neste artigo.
Listagem 11. Tipos complexos aninhados disponibilizados para uma página do AJAX ASP.NET.
if (typeof(Model.Address) === 'undefined')
{
Model.Address=gtc("Model.Address");
Model.Address.registerClass('Model.Address');
}
Model.Gender = function() { throw Error.invalidOperation(); }
Model.Gender.prototype = {Unknown: 0,Male: 1,Female: 2}
Model.Gender.registerEnum('Model.Gender', true);
Agora que você viu como criar serviços da Web e torná-los acessíveis a ASP.NET páginas AJAX, vamos dar uma olhada em como criar e usar proxies JavaScript para que os dados possam ser recuperados ou enviados para serviços da Web.
Criando proxies JavaScript
Chamar um serviço Web padrão (.NET ou outra plataforma) normalmente envolve a criação de um objeto proxy que protege você das complexidades do envio de mensagens de solicitação e resposta SOAP. Com ASP.NET chamadas de serviço Web AJAX, os proxies JavaScript podem ser criados e usados para chamar serviços facilmente sem se preocupar em serializar e desserializar mensagens JSON. Os proxies JavaScript podem ser gerados automaticamente usando o controle ASP.NET AJAX ScriptManager.
A criação de um proxy JavaScript que pode chamar Serviços Web é realizada usando a propriedade Serviços do ScriptManager. Essa propriedade permite definir um ou mais serviços que uma página AJAX ASP.NET pode chamar de forma assíncrona para enviar ou receber dados sem exigir operações de postback. Você define um serviço usando o controle AJAX ServiceReference
ASP.NET e atribuindo a URL do serviço Web à propriedade do Path
controle. A Listagem 12 mostra um exemplo de referência a um serviço chamado CustomersService.asmx.
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/CustomersService.asmx" />
</Services>
</asp:ScriptManager>
Listagem 12. Definindo um serviço Web usado em uma página AJAX do ASP.NET.
Adicionar uma referência ao CustomersService.asmx por meio do controle ScriptManager faz com que um proxy JavaScript seja gerado dinamicamente e referenciado pela página. O proxy é inserido usando a <marca de script> e carregado dinamicamente chamando o arquivo CustomersService.asmx e acrescentando /js ao final dele. O exemplo a seguir mostra como o proxy JavaScript é inserido na página quando a depuração está desabilitada em web.config:
<script src="CustomersService.asmx/js" type="text/javascript"></script>
> [! NOTE] Se você quiser ver o código proxy JavaScript real gerado, digite a URL para o Serviço Web .NET desejado na caixa de endereço do Internet Explorer e acrescente /js ao final dela.
Se a depuração estiver habilitada em web.config, uma versão de depuração do proxy JavaScript será inserida na página, conforme mostrado a seguir:
<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>
O proxy JavaScript criado pelo ScriptManager também pode ser inserido diretamente na página, em vez de referenciado usando o <atributo src da tag de script> . Isso pode ser feito definindo a propriedade InlineScript do controle ServiceReference como true (o padrão é false). Isso pode ser útil quando um proxy não é compartilhado em várias páginas e quando você deseja reduzir o número de chamadas de rede feitas ao servidor. Quando InlineScript é definido como true, o script de proxy não será armazenado em cache pelo navegador, portanto, o valor padrão de false é recomendado nos casos em que o proxy é usado por várias páginas em um aplicativo AJAX ASP.NET. Um exemplo de uso da propriedade InlineScript é mostrado a seguir:
<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>
Usando proxies JavaScript
Depois que um serviço Web é referenciado por uma página AJAX ASP.NET usando o controle ScriptManager, uma chamada pode ser feita para o serviço Web e os dados retornados podem ser manipulados usando funções de retorno de chamada. Um serviço Web é chamado referenciando seu namespace (se houver), nome da classe e nome do método Web. Todos os parâmetros passados para o serviço Web podem ser definidos junto com uma função de retorno de chamada que manipula os dados retornados.
Um exemplo de uso de um proxy JavaScript para chamar um método da Web chamado GetCustomersByCountry() é mostrado na Listagem 13. A função GetCustomersByCountry() é chamada quando um usuário final clica em um botão na página.
Listagem 13. Chamando um serviço Web com um proxy JavaScript.
function GetCustomerByCountry()
{
var country = $get("txtCountry").value;
InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
if (results != null)
{
CreateCustomersTable(results);
GetMap(results);
}
}
Essa chamada faz referência ao namespace InterfaceTraining, à classe CustomersService e ao método Web GetCustomersByCountry definidos no serviço. Ele passa um valor de país obtido de uma caixa de texto, bem como uma função de retorno de chamada chamada OnWSRequestComplete que deve ser invocada quando a chamada de serviço Web assíncrona retornar. OnWSRequestComplete manipula a matriz de objetos Customer retornados do serviço e os converte em uma tabela que é exibida na página. A saída gerada a partir da chamada é mostrada na Figura 1.
Figura 1: Dados de associação obtidos fazendo uma chamada AJAX assíncrona para um serviço Web. (Clique para ver a imagem em tamanho real)
Os proxies JavaScript também podem fazer chamadas unidirecionais para serviços Web nos casos em que um método Web deve ser chamado, mas o proxy não deve esperar por uma resposta. Por exemplo, talvez você queira chamar um serviço Web para iniciar um processo, como um fluxo de trabalho, mas não aguardar um valor retornado do serviço. Nos casos em que uma chamada unidirecional precisa ser feita para um serviço, a função de retorno de chamada mostrada na Listagem 13 pode simplesmente ser omitida. Como nenhuma função de retorno de chamada é definida, o objeto proxy não aguardará que o serviço Web retorne dados.
Manipulando erros
Os retornos de chamada assíncronos para os serviços da Web podem encontrar diferentes tipos de erros, como a rede estar inativa, o serviço da Web estar indisponível ou uma exceção sendo retornada. Felizmente, os objetos proxy JavaScript gerados pelo ScriptManager permitem que vários retornos de chamada sejam definidos para lidar com erros e falhas, além do retorno de chamada de êxito mostrado anteriormente. Uma função de retorno de chamada de erro pode ser definida imediatamente após a função de retorno de chamada padrão na chamada para o Método da Web, como mostrado na Listagem 14.
Listagem 14. Definir uma função de retorno de chamada de erro e exibir erros.
function GetCustomersByCountry()
{
var country = $get("txtCountry").value;
InterfaceTraining.CustomersService.GetCustomersByCountry(country,
OnWSRequestComplete, OnWSRequestFailed);
}
function OnWSRequestFailed(error)
{
alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
"Error: " + error.get_message() + "/r/n" +
"Status Code: " + error.get_statusCode() + "/r/n" +
"Exception Type: " + error.get_exceptionType() + "/r/n" +
"Timed Out: " + error.get_timedOut());
}
Quaisquer erros que ocorram quando o serviço Web é chamado acionarão a função de retorno de chamada OnWSRequestFailed() a ser chamada, o que aceita um objeto que representa o erro como um parâmetro. O objeto error expõe várias funções diferentes para determinar a causa do erro, bem como se a chamada atingiu ou não o tempo limite. A Listagem 14 mostra um exemplo de uso das diferentes funções de erro e a Figura 2 mostra um exemplo da saída gerada pelas funções.
Figura 2: Saída gerada chamando ASP.NET funções de erro AJAX. (Clique para ver a imagem em tamanho real)
Manipulando dados XML retornados de um serviço Web
Anteriormente, você viu como um método da Web poderia retornar dados XML brutos usando o atributo ScriptMethod junto com sua propriedade ResponseFormat. Quando ResponseFormat é definido como ResponseFormat.Xml, os dados retornados do serviço Web são serializados como XML em vez de JSON. Isso pode ser útil quando os dados XML precisam ser passados diretamente para o cliente para processamento usando JavaScript ou XSLT. No momento, o Internet Explorer 5 ou superior fornece o melhor modelo de objeto do lado do cliente para analisar e filtrar dados XML devido ao seu suporte interno para MSXML.
Recuperar dados XML de um serviço Web não é diferente de recuperar outros tipos de dados. Comece chamando o proxy JavaScript para chamar a função apropriada e definir uma função de retorno de chamada. Depois que a chamada retornar, você poderá processar os dados na função de retorno de chamada.
A Listagem 15 mostra um exemplo de chamada de um método da Web chamado GetRssFeed() que retorna um objeto XmlElement. GetRssFeed() aceita um único parâmetro que representa a URL para o feed RSS recuperar.
Listagem 15. Trabalhando com dados XML retornados de um serviço Web.
function GetRss()
{
InterfaceTraining.DemoService.GetRssFeed(
"https://blogs.interfacett.com/dan-wahlins-blog/rss.xml",
OnWSRequestComplete);
}
function OnWSRequestComplete(result)
{
if (document.all) //Filter for IE DOM since other browsers are limited
{
var items = result.selectNodes("//item");
for (var i=0;i<items.length;i++)
{
var title = items[i].selectSingleNode("title").text;
var href = items[i].selectSingleNode("link").text;
$get("divOutput").innerHTML +=
"<a href='" + href + "'>" + title + "</a><br/>";
}
}
else
{
$get("divOutput").innerHTML = "RSS only available in IE5+";
}
}
Este exemplo passa uma URL para um feed RSS e processa os dados XML retornados na função OnWSRequestComplete(). OnWSRequestComplete() primeiro verifica se o navegador é o Internet Explorer para saber se o analisador MSXML está disponível ou não. Se for, uma instrução XPath será usada para localizar todas as <tags de item> no feed RSS. Cada item é então iterado e as tags de título> e <link> associadas <são localizadas e processadas para exibir os dados de cada item. A Figura 3 mostra um exemplo da saída gerada ao fazer uma chamada AJAX ASP.NET por meio de um proxy JavaScript para o método da Web GetRssFeed().
Manipulando tipos complexos
Os tipos complexos aceitos ou retornados por um serviço Web são expostos automaticamente por meio de um proxy JavaScript. No entanto, os tipos complexos aninhados não são diretamente acessíveis no lado do cliente, a menos que o atributo GenerateScriptType seja aplicado ao serviço, conforme discutido anteriormente. Por que você deseja usar um tipo complexo aninhado no lado do cliente?
Para responder a essa pergunta, suponha que uma página AJAX ASP.NET exiba dados do cliente e permita que os usuários finais atualizem o endereço de um cliente. Se o serviço Web especificar que o tipo Address (um tipo complexo definido em uma classe CustomerDetails) pode ser enviado ao cliente, o processo de atualização poderá ser dividido em funções separadas para melhor reutilização do código.
Figura 3: Saída criada a partir da chamada de um serviço Web que retorna dados RSS. (Clique para ver a imagem em tamanho real)
A Listagem 16 mostra um exemplo de código do lado do cliente que chama um objeto Address definido em um namespace Model, preenche-o com dados atualizados e o designa à propriedade Address de um objeto CustomerDetails. O objeto CustomerDetails é então passado para o Serviço Web para processamento.
Listagem 16. Usando tipos complexos aninhados
function UpdateAddress()
{
var cust = new Model.CustomerDetails();
cust.CustomerID = $get("hidCustomerID").value;
cust.Address = CreateAddress();
InterfaceTraining.DemoService.UpdateAddress(cust,OnWSUpdateComplete);
}
function CreateAddress()
{
var addr = new Model.Address();
addr.Street = $get("txtStreet").value;
addr.City = $get("txtCity").value;
addr.State = $get("txtState").value;
return addr;
}
function OnWSUpdateComplete(result)
{
alert("Update " + ((result)?"succeeded":"failed")+ "!");
}
Criando e usando métodos de página
Os serviços da Web fornecem uma excelente maneira de expor serviços reutilizáveis a uma variedade de clientes, incluindo páginas AJAX ASP.NET. No entanto, pode haver casos em que uma página precise recuperar dados que nunca serão usados ou compartilhados por outras páginas. Nesse caso, criar um arquivo .asmx para permitir que a página acesse os dados pode parecer um exagero, pois o serviço é usado apenas por uma única página.
ASP.NET AJAX fornece outro mecanismo para fazer chamadas semelhantes a serviços da Web sem criar arquivos .asmx autônomos. Isso é feito usando uma técnica conhecida como "métodos de página". Os métodos de página são métodos estáticos (compartilhados em VB.NET) inseridos diretamente em uma página ou arquivo ao lado de código que têm o atributo WebMethod aplicado a eles. Ao aplicar o atributo WebMethod, eles podem ser chamados usando um objeto JavaScript especial chamado PageMethods que é criado dinamicamente em tempo de execução. O objeto PageMethods atua como um proxy que protege você do processo de serialização/desserialização JSON. Observe que, para usar o objeto PageMethods, você deve definir a propriedade EnablePageMethods do ScriptManager como true.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>
A Listagem 17 mostra um exemplo de definição de dois métodos de página em uma classe ASP.NET code-side. Esses métodos recuperam dados de uma classe de camada de negócios localizada na pasta App_Code do site.
Listagem 17. Definindo métodos de página.
[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
return Biz.BAL.GetCustomersByID(id);
}
Quando o ScriptManager detecta a presença de Métodos da Web na página, ele gera uma referência dinâmica ao objeto PageMethods mencionado anteriormente. A chamada de um método da Web é realizada referenciando a classe PageMethods seguida pelo nome do método e quaisquer dados de parâmetro necessários que devem ser passados. A Listagem 18 mostra exemplos de chamada dos dois métodos de página mostrados anteriormente.
Listagem 18. Chamando métodos de página com o objeto JavaScript PageMethods.
function GetCustomerByCountry()
{
var country = $get("txtCountry").value;
PageMethods.GetCustomersByCountry(country, OnWSRequestComplete);
}
function GetCustomerByID()
{
var custID = $get("txtCustomerID").value;
PageMethods.GetCustomersByID(custID, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
var searchResults = $get("searchResults");
searchResults.control.set_data(results);
if (results != null) GetMap(results[0].Country,results);
}
O uso do objeto PageMethods é muito semelhante ao uso de um objeto proxy JavaScript. Primeiro, especifique todos os dados de parâmetro que devem ser passados para o método de página e, em seguida, defina a função de retorno de chamada que deve ser chamada quando a chamada assíncrona retornar. Um retorno de chamada de falha também pode ser especificado (consulte a Listagem 14 para obter um exemplo de manipulação de falhas).
O AutoCompleteExtender e o ASP.NET AJAX Toolkit
O ASP.NET AJAX Toolkit (disponível em https://github.com/DevExpress/AjaxControlToolkit) oferece vários controles que podem ser usados para acessar os serviços da Web. Especificamente, o kit de ferramentas contém um controle útil chamado AutoCompleteExtender
que pode ser usado para chamar serviços Web e mostrar dados em páginas sem escrever nenhum código JavaScript.
O controle AutoCompleteExtender pode ser usado para estender a funcionalidade existente de uma caixa de texto e ajudar os usuários a localizar mais facilmente os dados que estão procurando. À medida que digitam em uma caixa de texto, o controle pode ser usado para consultar um serviço Web e mostra os resultados abaixo da caixa de texto dinamicamente. A Figura 4 mostra um exemplo de uso do controle AutoCompleteExtender para exibir IDs de clientes para um aplicativo de suporte. À medida que o usuário digita caracteres diferentes na caixa de texto, itens diferentes serão mostrados abaixo dela com base em sua entrada. Os usuários podem selecionar a ID do cliente desejada.
O uso do AutoCompleteExtender em uma página AJAX ASP.NET requer que o assembly AjaxControlToolkit.dll seja adicionado à pasta bin do site. Depois que o assembly do kit de ferramentas for adicionado, você desejará referenciá-lo em web.config para que os controles que ele contém estejam disponíveis para todas as páginas em um aplicativo. Isso pode ser feito adicionando a seguinte marca na marca de <controles> do web.config:
<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>
Nos casos em que você só precisa usar o controle em uma página específica, você pode referenciá-lo adicionando a diretiva Reference à parte superior de uma página, conforme mostrado a seguir, em vez de atualizar web.config:
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>
Figura 4: Usando o controle AutoCompleteExtender. (Clique para ver a imagem em tamanho real)
Depois que o site tiver sido configurado para usar o ASP.NET AJAX Toolkit, um controle AutoCompleteExtender poderá ser adicionado à página da mesma forma que você adicionaria um controle de servidor ASP.NET regular. A Listagem 19 mostra um exemplo de como usar o controle para chamar um serviço da Web.
Listagem 19. Usando o controle AutoCompleteExtender do ASP.NET AJAX Toolkit.
<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
ServicePath="~/CustomersService.asmx"
TargetControlID="txtCustomerID" />
O AutoCompleteExtender tem várias propriedades diferentes, incluindo a ID padrão e as propriedades runat encontradas nos controles do servidor. Além disso, ele permite que você defina quantos caracteres um usuário final digita antes que o serviço Web seja consultado em busca de dados. A propriedade MinimumPrefixLength mostrada na Listagem 19 faz com que o serviço seja chamado sempre que um caractere é digitado na caixa de texto. Você deve ter cuidado ao definir esse valor, pois cada vez que o usuário digitar um caractere, o Serviço Web será chamado para pesquisar valores que correspondam aos caracteres na caixa de texto. O serviço Web a ser chamado, bem como o método Web de destino, são definidos usando as propriedades ServicePath e ServiceMethod, respectivamente. Por fim, a propriedade TargetControlID identifica a qual caixa de texto conectar o controle AutoCompleteExtender.
O serviço Web que está sendo chamado deve ter o atributo ScriptService aplicado conforme discutido anteriormente e o método Web de destino deve aceitar dois parâmetros chamados prefixText e count. O parâmetro prefixText representa os caracteres digitados pelo usuário final e o parâmetro count representa quantos itens devem ser retornados (o padrão é 10). A Listagem 20 mostra um exemplo do método da Web GetCustomerIDs chamado pelo controle AutoCompleteExtender mostrado anteriormente na Listagem 19. O Método Web chama um método de camada de negócios que, por sua vez, chama um método de camada de dados que manipula a filtragem dos dados e retorna os resultados correspondentes. O código para o método da camada de dados é mostrado na Listagem 21.
Listagem 20. Filtrando dados enviados do controle AutoCompleteExtender.
[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count)
{
return Biz.BAL.GetCustomerIDs(prefixText, count);
}
Listagem 21. Filtrando resultados com base na entrada do usuário final.
public static string[] GetCustomerIDs(string prefixText, int count)
{
//Customer IDs cached in _CustomerIDs field to improve performance
if (_CustomerIDs == null)
{
List<string> ids = new List<string>();
//SQL text used for simplicity...recommend using sprocs
string sql = "SELECT CustomerID FROM Customers";
DbConnection conn = GetDBConnection();
conn.Open();
DbCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
DbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
ids.Add(reader["CustomerID"].ToString());
}
reader.Close();
conn.Close();
_CustomerIDs = ids.ToArray();
}
int index = Array.BinarySearch(_CustomerIDs, prefixText, new CaseInsensitiveComparer());
//~ is bitwise complement (reverse each bit)
if (index < 0) index = ~index;
int matchingCount;
for (matchingCount = 0; matchingCount < count && index + matchingCount < _CustomerIDs.Length; matchingCount++)
{
if (!_CustomerIDs[index + matchingCount].StartsWith(prefixText, StringComparison.CurrentCultureIgnoreCase))
{
break;
}
}
String[] returnValue = new string[matchingCount];
if (matchingCount > 0)
{
Array.Copy(_CustomerIDs, index, returnValue, 0, matchingCount);
}
return returnValue;
}
Conclusão
ASP.NET AJAX fornece excelente suporte para chamar serviços Web sem escrever muito código JavaScript personalizado para lidar com as mensagens de solicitação e resposta. Neste artigo, você viu como habilitar o .NET Web Services para AJAX para permitir que eles processem mensagens JSON e como definir proxies JavaScript usando o controle ScriptManager. Você também viu como os proxies JavaScript podem ser usados para chamar serviços da Web, lidar com tipos simples e complexos e lidar com falhas. Por fim, você viu como os métodos de página podem ser usados para simplificar o processo de criação e realização de chamadas de serviço Web e como o controle AutoCompleteExtender pode fornecer ajuda aos usuários finais enquanto eles digitam. Embora o UpdatePanel disponível no ASP.NET AJAX certamente seja o controle de escolha para muitos programadores AJAX devido à sua simplicidade, saber como chamar Web Services por meio de proxies JavaScript pode ser útil em muitas aplicações.
Biografia
Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET e XML Web Services) é instrutor de desenvolvimento .NET e consultor de arquitetura na Interface Technical Training (http://www.interfacett.com). Dan fundou o site XML for ASP.NET Developers (www.XMLforASP.NET), faz parte do INETA Speaker's Bureau e é palestrante em várias conferências. Dan é coautor do Professional Windows DNA (Wrox), ASP.NET: Dicas, Tutoriais e Código (Sams), ASP.NET 1.1 Insider Solutions, Professional ASP.NET 2.0 AJAX (Wrox), ASP.NET 2.0 MVP Hacks e autor de XML para desenvolvedores ASP.NET (Sams). Quando não está escrevendo códigos, artigos ou livros, Dan gosta de escrever e gravar músicas e jogar golfe e basquete com sua esposa e filhos.
Scott Cate trabalha com tecnologias da Web da Microsoft desde 1997 e é o presidente da myKB.com (www.myKB.com), onde se especializou em escrever aplicativos baseados em ASP.NET com foco em soluções de software de base de conhecimento. Scott pode ser contatado por e-mail ou scott.cate@myKB.com seu blog em ScottCate.com