Noções básicas sobre os serviços Web do AJAX ASP.NET
por Scott Cate
Os Serviços Web são parte integrante do .NET Framework que fornece uma solução multiplataforma para trocar dados entre sistemas distribuídos. Embora os Serviços Web normalmente sejam 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 ASP.NET AJAX 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 Serviços Web são parte integrante do .NET Framework que fornece uma solução multiplataforma para trocar dados entre sistemas distribuídos. Embora os Serviços Web normalmente sejam 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 ASP.NET AJAX 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 ASP.NET controle AJAX UpdatePanel forneça uma maneira simples de a AJAX habilitar qualquer página ASP.NET, pode haver momentos em que você precisa acessar dados dinamicamente 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 Kit de Ferramentas AJAX chamado AutoCompleteExtender. Os tópicos abordados incluem a definição de Serviços Web habilitados para AJAX, a criação de proxies de cliente e a chamada de Serviços Web com JavaScript. Você também verá como as chamadas do Serviço Web podem ser feitas diretamente para ASP.NET métodos de página.
Configuração de Serviços Web
Quando um novo projeto de Site é criado com o Visual Studio 2008, o arquivo web.config tem uma série de novas adições que podem não ser familiares para 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 <httpHandlers>
elemento em web.config que afeta as chamadas do Serviço 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 toda a funcionalidade principal usada pelo ASP.NET AJAX.
Listagem 1. configuração do manipulador de serviço Web ASP.NET 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 do AJAX ASP.NET para os Serviços Web do .NET usando um proxy do Serviço Web JavaScript. ASP.NET AJAX envia mensagens JSON para serviços Web em oposição às chamadas SOAP (Simple Object Access Protocol) padrão normalmente associadas aos Serviços Web. Isso resulta em mensagens de solicitação e resposta menores em geral. Ele também permite um processamento mais eficiente do lado do cliente de dados, uma vez que a biblioteca JavaScript ASP.NET AJAX é otimizada para trabalhar com objetos JSON. Listagem 2 e Listagem 3 mostram exemplos de solicitação do Serviço Web e mensagens de resposta serializadas no formato JSON. A mensagem de solicitação mostrada na Listagem 2 passa um parâmetro de país com um valor "Bélgica", 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"}
> [! OBSERVAÇÃO] 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 por meio de 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 AJAX-Enabled
A estrutura ASP.NET AJAX fornece várias maneiras diferentes de chamar serviços Web. Você pode usar o controle AutoCompleteExtender (disponível no ASP.NET Kit de Ferramentas AJAX) ou JavaScript. No entanto, antes de chamar um serviço, você precisa habilitá-lo para que ele possa ser chamado pelo código de script do cliente.
Independentemente de você ser novo ou não em ASP.NET Serviços Web, você achará simples criar e habilitar serviços habilitado para AJAX. O .NET Framework dá suporte à criação de serviços Web ASP.NET desde sua versão inicial em 2002 e as extensões AJAX do ASP.NET fornecem funcionalidade adicional do AJAX que se baseia no conjunto padrão de recursos do .NET Framework. O Visual Studio .NET 2008 Beta 2 tem suporte interno para criar arquivos do 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 objetos Customer. O valor de 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 de ASP.NET aplicativos AJAX prontos para uso. Para permitir que chamadas JSON sejam feitas, você precisa aplicar o atributo da ScriptService
Extensão ASP.NET AJAX à classe de Serviço Web. 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 Web chamada CustomersService.
Listagem 5. Usando o atributo ScriptService para habilitar um serviço Web habilitado 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 manipula 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 ASP.NET AJAX que precisa ser definido em um Serviço Web .NET para que ele seja usado por páginas ASP.NET AJAX. 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 aceita por um Método Web precisa ser alterado para GET, quando um Método Web precisa retornar dados XML brutos na forma de um XmlDocument
objeto ou XmlElement
ou quando os dados retornados de um serviço sempre devem 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 Web convertidos em parâmetros QueryString. A propriedade UseHttpGet usa como padrão false e só deve ser definida true
como 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 como usar o 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 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 métodos 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 RSS feed de um site remoto e devolvê-lo como um objeto XmlDocument ou XmlElement. O processamento dos dados XML pode ocorrer no cliente.
A listagem 7 mostra um exemplo de como usar a propriedade ResponseFormat para especificar que os dados XML devem ser retornados de um Método 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 cadeias de caracteres retornadas de um Método Web, são serializados como XML quando a ResponseFormat
propriedade é definida ResponseFormat.Xml
como . Quando XmlSerializeString
é definido como true
, todos os tipos retornados de um Método Web são serializados como XML, incluindo tipos de cadeia de caracteres. Se a propriedade ResponseFormat tiver um valor da ResponseFormat.Json
propriedade XmlSerializeString for ignorada.
A listagem 8 mostra um exemplo de como usar a propriedade XmlSerializeString para forçar 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 de chamar o Método 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 facilmente consumido por clientes ASP.NET AJAX de maneira cruzada, as propriedades ResponseFormat e XmlSerializeString podem ser utilizadas quando aplicativos cliente como a 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 como retornar um tipo complexo chamado Cliente de um Serviço Web. A classe Customer define vários tipos simples diferentes internamente, como propriedades como FirstName e LastName. Tipos complexos usados como um parâmetro de entrada ou tipo de retorno em um Método Web habilitado para AJAX são serializados automaticamente em JSON antes de serem enviados para o lado do cliente. No entanto, 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 do cliente, o atributo generateScriptType ASP.NET AJAX pode ser adicionado ao Serviço Web. Por exemplo, a classe CustomerDetails mostrada na Listagem 9 contém 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 por meio do 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 Web deve estar disponível no lado do cliente, o atributo GenerateScriptType mencionado anteriormente pode ser usado (consulte 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 de Serviço Web ou a métodos Web específicos acima.
Listando 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
}
Aplicando o GenerateScriptType
atributo ao Serviço Web, os tipos Endereço e Gênero serão disponibilizados automaticamente para uso pelo lado do cliente ASP.NET código JavaScript AJAX. Um exemplo do JavaScript que é gerado e enviado automaticamente ao cliente adicionando o atributo GenerateScriptType em um serviço Web é mostrado na Listagem 11. Você verá como usar tipos complexos aninhados mais adiante no artigo.
Listagem 11. Tipos complexos aninhados disponibilizados para uma página ASP.NET AJAX.
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 Web e torná-los acessíveis para 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 aos Serviços 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 de envio de mensagens de solicitação e resposta SOAP. Com ASP.NET chamadas do Serviço Web AJAX, os proxies JavaScript podem ser criados e usados para chamar facilmente serviços sem se preocupar em serializar e desserializar mensagens JSON. Os proxies JavaScript podem ser gerados automaticamente usando o ASP.NET controle 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 que você defina um ou mais serviços que uma página ASP.NET AJAX pode chamar de forma assíncrona para enviar ou receber dados sem a necessidade de operações de postback. Você define um serviço usando o ASP.NET controle AJAX ServiceReference
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 ASP.NET AJAX.
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 é desabilitada no web.config:
<script src="CustomersService.asmx/js" type="text/javascript"></script>
>[! OBSERVAÇÃO] 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 Explorer da Internet e acrescente /js ao final dele.
Se a depuração estiver habilitada no 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 marca 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 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 como usar a 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 ASP.NET AJAX usando o controle ScriptManager, uma chamada pode ser feita para o Serviço Web e os dados retornados podem ser tratados usando funções de retorno de chamada. Um Serviço Web é chamado fazendo referência ao namespace (se houver), ao nome da classe e ao 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 como usar um proxy JavaScript para chamar um Método 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 definido 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 chamada OnWSRequestComplete que deve ser invocada quando a chamada de Serviço Web assíncrona retorna. OnWSRequestComplete manipula a matriz de objetos Customer retornados do serviço e os converte em uma tabela exibida na página. A saída gerada a partir da chamada é mostrada na Figura 1.
Figura 1: Associar dados obtidos fazendo uma chamada AJAX assíncrona a um serviço Web. (Clique para exibir 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 em 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
Retornos de chamada assíncronos para serviços Web podem encontrar diferentes tipos de erros, como a rede inoperante, o Serviço Web indisponível ou uma exceção que está 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 Web, conforme mostrado na Listagem 14.
Listagem 14. Definindo uma função de retorno de chamada de erro e exibindo 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());
}
Todos os erros que ocorrem quando o Serviço Web é chamado dispararão a função de retorno de chamada OnWSRequestFailed() a ser chamada, que aceita um objeto que representa o erro como um parâmetro. O objeto de erro 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 como usar as 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 exibir a imagem em tamanho real)
Manipulando dados XML retornados de um serviço Web
Anteriormente, você viu como um Método 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, a Internet Explorer 5 ou superior fornece o melhor modelo de objeto do lado do cliente para analisar e filtrar dados XML devido ao suporte interno para MSXML.
Recuperar dados XML de um Serviço Web não é diferente de recuperar outros tipos de dados. Comece invocando 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 como chamar um Método Web chamado GetRssFeed() que retorna um objeto XmlElement. GetRssFeed() aceita um único parâmetro que representa a URL do feed RSS a ser recuperado.
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 é internet Explorer saber se o analisador MSXML está disponível ou não. Se for, uma instrução XPath será usada para localizar todas as <marcas de item> no feed RSS. Cada item é iterado e as marcas 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 de fazer uma chamada AJAX ASP.NET por meio de um proxy JavaScript para o Método Web GetRssFeed().
Manipulando tipos complexos
Tipos complexos aceitos ou retornados por um Serviço Web são expostos automaticamente por meio de um proxy JavaScript. No entanto, 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 ASP.NET AJAX 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 de endereço (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 de código.
Figura 3: saída criando com base na chamada de um serviço Web que retorna dados RSS. (Clique para exibir a imagem em tamanho real)
A listagem 16 mostra um exemplo de código do lado do cliente que invoca um objeto Address definido em um namespace Model, preenche-o com dados atualizados e o atribui à 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 Web fornecem uma excelente maneira de expor serviços reutilizáveis a uma variedade de clientes, incluindo ASP.NET páginas AJAX. No entanto, pode haver casos em que uma página precisa 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 ao Serviço Web sem criar arquivos .asmx autônomos. Isso é feito usando uma técnica chamada "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. Aplicando o atributo WebMethod, eles podem ser chamados usando um objeto JavaScript especial chamado PageMethods que é criado dinamicamente no runtime. 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 de código ao lado. 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 ScriptManager detecta a presença de Métodos Web na página, ele gera uma referência dinâmica ao objeto PageMethods mencionado anteriormente. Chamar um Método Web é feito referenciando a classe PageMethods seguida pelo nome do método e todos os dados de parâmetro necessários que devem ser passados. A listagem 18 mostra exemplos de como chamar os dois métodos de página mostrados anteriormente.
Listagem 18. Métodos de página de chamada 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 Listagem 14 para obter um exemplo de tratamento de falhas).
O AutoCompleteExtender e o ASP.NET AJAX Toolkit
O kit de ferramentas ASP.NET AJAX (disponível em https://www.devexpress.com/Products/AJAX-Control-Toolkit) oferece vários controles que podem ser usados para acessar os Serviços 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 dados mais facilmente que estão procurando. Conforme eles 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 como usar o controle AutoCompleteExtender para exibir ids de cliente para um aplicativo de suporte. À medida que o usuário digita caracteres diferentes na caixa de texto, diferentes itens serão mostrados abaixo com base em sua entrada. Em seguida, os usuários podem selecionar a ID de cliente desejada.
O uso do AutoCompleteExtender em uma página ASP.NET AJAX exige que o assembly AjaxControlToolkit.dll seja adicionado à pasta bin do Site. Depois que o assembly do kit de ferramentas tiver sido 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 em seguida, em vez de atualizar web.config:
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>
Figura 4: usando o controle AutoCompleteExtender. (Clique para exibir a imagem em tamanho real)
Depois que o Site tiver sido configurado para usar o kit de ferramentas ASP.NET AJAX, 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 Web.
Listagem 19. Usando o ASP.NET controle AutoCompleteExtender do Kit de Ferramentas AJAX.
<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 deles, ele permite que você defina quantos caracteres um usuário final digita antes que o Serviço Web seja consultado quanto a 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ê desejará ter cuidado ao definir esse valor, pois sempre 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 caixa de texto à qual 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 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 o retorno dos resultados correspondentes. O código para o método de camada de dados é mostrado na Listagem 21.
Listando 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 oferece excelente suporte para chamar Serviços Web sem escrever muitos códigos JavaScript personalizados para lidar com as mensagens de solicitação e resposta. Neste artigo, você viu como habilitar o .NET Web Services para habilitar o AJAX para 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 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 criação de chamadas de Serviço Web e como o controle AutoCompleteExtender pode fornecer ajuda aos usuários finais conforme eles digitam. Embora o UpdatePanel disponível no ASP.NET AJAX certamente será o controle de escolha para muitos programadores AJAX devido à sua simplicidade, saber como chamar serviços Web por meio de proxies JavaScript pode ser útil em muitos aplicativos.
Biografia
Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web Services) é um instrutor de desenvolvimento do .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), está no Gabinete do Orador do INETA e fala em várias conferências. Dan foi co-autor de DNA profissional do Windows (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 XML criado para desenvolvedores de ASP.NET (Sams). Quando ele não está escrevendo código, 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 Microsoft Web desde 1997 e é presidente de myKB.com (www.myKB.com), onde é especialista em escrever aplicativos baseados em ASP.NET focados em soluções de Software de Base de Conhecimento. Scott pode ser contatado por e-mail em scott.cate@myKB.com ou seu blog em ScottCate.com