Programação em Asp.Net - parte II
Autor: Luís Abreu
Conteúdo: Introdução à programação em Asp.Net 2.0
Ferramentas: Visual Web Dev Express
Após no primeiro artigo termos apresentado os novos controlos de navegação, hoje vamos prosseguir a nossa odisseia e vamos falar acerca do novo mecanismo de postback intrínseco à nova plataforma Asp.Net 2.0. Com este mecanismo já é possível obtermos dados a partir do servidor sem termos de efectuar um postback, fazendo assim com que não seja necessário refrescar toda a página para obter dados a partir do servidor.
Este artigo irá apresentar os principais passos que devem ser dados por parte do programador para poder utilizar esta nova funcionalidade. Para tal, iremos demonstrar a utilização directa deste mecanismo a partir de uma página aspx. Todos os principios apresentados podem também ser aplicados a controlos.
Introdução história
A tentativa de refrescar parte de uma página não é nova...Presumo eu que podemos considerar que o primeiro passo dado nesse sentido já é muito antigo: estou a falar dos frames! Claro que os frames nunca satisfizeram por completo os autores de páginas e, como consequência dessa insatisfação, foram aparecendo outros mecanismos mais interessantes que permitiam esse tipo de interacção.
Profissionalmente sempre desenvolvi sobre a plataforma Microsoft. Por isso, lembro-me (mais ou menos bem) de duas das opções que permitiam este tipo de operações: estou a falar do Remote Scripting e do "Microsoft.XmlHttp" (vamos chamá-lo assim e esperemos que ele não fique ofendido ;) ). Para além destes mecanismos, existia ainda a hipótese de desenvolvermos um controlo que efectuasse esse tipo de operações (algo que também cheguei a fazer utilizando para isso um objecto COM e a API do Wininet :) ). Cada uma destas aproximações oferecia vantagens e desvantagens.
Apesar de todas as funcionalidades das opções anteriores, existiam ainda alguns problemas a nível de integração entre essas tecnologias e as páginas Asp.Net. Isto porque, na minha opinião, este tipo de tecnologias não se adaptam lá muito bem com o modelo existente a nível das páginas aspx. Ao verificar isto, a Microsoft optou por integrar um mecanismo de callbacks na nova versão 2.0 da framework.
Utilização prática
Como é óbvio, esta nova funcionalidade implica uma convivência entre o código cliente e o código servidor. Para efectuarmos o callback temos de seguir um conjunto de passos muito simples:
- Implementar o interface ICallbackEventHandler a nível do controlo (ou da página - afinal de contas uma página também é um controlo!) que pretende utilizar esta funcionalidade;
- Associar um método que inicia o callback a um evento cliente;
- Definir a função que irá ser evocada quando a informação tiver sido obtida a partir do servidor;
- Definir uma função que irá ser chamada se ocorrer algum erro (opcional);
Vamos então analisar cada um destes passos individualmente.
Implementar o interface ICallbackEventHandler
A implementação deste interface é essencial. Todos os controlos que necessitam de participar neste tipo de eventos devem implementar este interface pois é através dele que a framework consegue efectuar o processamento efectivo do callback. Quando é recebido um pedido, a framework começa por inspeccionar o modo de postback de forma a perceber se está num postback "real" ou numa situação de callback. Se estivermos numa situação de callback, então o HTTP handler define a propriedade IsCallback com o valor true e identifica o controlo que efectuou o pedido de callback através da entrada __CALLBACK da colecção associada ao objecto Request.
Se o controlo implementar o interface ICallbackEventHandler, então a framework evoca o único método deste interface (RaiseCallbackEvent) de forma a que o controlo possa efectuar o processamento da informação. O método RaiseCallbackEvent possui a seguinte assinatura (C#):
string RaiseCallbackEvent( string eventArgument);
Como é possível aferir, o método apenas pode receber uma string (que será enviada do lado cliente) e, após efectuar o processamento, apenas pode devolver uma string. Se for necessário passar vários dados, então será necessário concatenar essa informação (utilizando os métodos já tradicionais neste tipo de operações: XML, concatenção através da junção dos valores utilizando um determinado caracter como separador, etc.). Já agora, e apenas como informação suplementar, convém referir que o parâmetro eventArgument (proveniente da parte cliente) está armazenado na entrada __CALLBACKPARAM associada ao objecto Request, e que o elemento que originou o postback é identificado pela entrada __CALLBACKID.
Nota: se o leitor for um curioso nato como eu, então poderá validar esta informação utilizando para tal a propriedade Request da classe Page: this.Request.Params["__CALLBACKPARAM"].
Para terminarmos este tópico queria ainda referir que temos de ter algum cuidado a nível dos eventos/métodos tradicionais da página pois estes continuam a ser evocados (por isso é que o evento Load verifica se está em callback antes de executar código).
Definir o método cliente que inicia o callback
Como o leitor já deve estar a desconfiar, após configurarmos o nosso controlo para receber pedidos de callback, temos de inicializar esse pedido de alguma forma. Bem, parece-me óbvio afirmar que esse pedido deverá ser iniciado no lado cliente evocando uma função da framework (do lado cliente) que irá, por sua vez, iniciar a comunicação com o lado servidor para obter os dados pretendidos através do mecanismo descrito no parágrafo anterior.
A função javascript que inicia o pedido de callback chama-se WebForm_DoCallback. Para além desse método, existe também um outro método muito importante para o correcto funcionamento do mecanismo de callback: WebForm_InitCallback. Como é possível verificar através do excerto seguinte, ambos estes métodos são introduzidos na página através das seguintes instruções de javascript:
<script type="text/javascript">
<!--
var theForm = document.forms['form1'];
function __doPostBack(eventTarget, eventArgument) {
if (theForm.onsubmit == null || theForm.onsubmit()) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
// -->
</script>
<script src="WebResource.axd?a=s&r=WebForms.js&t=632269876384097872" type="text/javascript"></script>
Os mais curiosos deverão estar a interrogar-se sobre o significado do valor definido no atributo src do jscript: WebResource.axd. Bem, pelo menos eu fiquei bastante curioso. À primeira vista, conseguimos distinguir um valor mais ou menos conhecido: estou a referir-me ao ficheiro WebForms.js (digo conhecido porque pelo menos termina em js, o que indica tratar-se de um ficheiro de javascript). Por outro lado, a extensão axd parece-nos indicar que estamos a falar de uma handler. Este facto confirma-se após uma consulta ao ficheiro machine.config, onde podemos encontrar entradas para estes itens na secção httpHandlers. O artigo Handling Client Files in Asp.Net Whidbey oferece uma explicação interessante sobre este assunto. De interesse para a nossa discussão é o ficheiro WebForms.js, que contém a lógica cliente que permite despoletar o processo de callback. Se, como eu , estão à procura do ficheiro na vossa drive, então não procurem mais! É que a partir da nova versão, os ficheiros de jscript passaram a estar embebidos no interior das próprias dlls sendo utilizado a nova handler (WebResource.axd) para obter acesso a esses ficheiros(neste caso, poderão encontrar o ficheiro na System.Web.dll).
A consulta deste ficheiro de javascript através do valioso .Net Reflector permite-nos chegar à conclusão de que o mecanismo de callback é implementado à custa do nosso velho conhecido MSXml. O tipo de pedido depende, como é possível aferir, do tamanho da mensagem. Num próximo artigo iremos abordar de uma forma mais aprofundada esta nova handler muito importante para a utilização de recursos.
Tal como acontecia na versão anterior (a nível do mecanismo de postback), a forma correcta de introduzir estes elementos na página consiste em utilizar uma nova função da API da classe Page: GetCallbackEventReference. Esta função contém várias overloads, podendo receber os seguintes parâmetros (nome dos parâmetros baseados nos nomes contidos na documentação beta que acompanha as express tools):
- Control: referência ao controlo que implementa o interface ICallbackEventHandler;
- Target: caso não seja especificado nenhum controlo, este parâmetro identifica o Id do controlo que implementa o interface ICallbackEventHandler;
- argument: Indica o valor que irá ser passado para o servidor e que irá ser utilizado como argumento do método RaiseCallbackEvent;
- clientCallback: indica o nome do método que deverá ser evocado aquando da recepção da resposta proveniente do callback iniciado;
- context: define um valor que irá ser passado ao método definido em clientCallback (como segundo parâmetro desse método);
- clientErrorCallback: nome do método que deverá ser executado em casso de erro.
O primeiro sample que acompanha este artigo define o evento de callback através das instruções seguintes (lado servidor):
if (this.Request.Browser.SupportsCallback)
{
string jscript = this.GetCallbackEventReference(this,
"document.all.txt.value",
"UpdateLocalidade",
"null",
"ProcessError");
_test.Attributes["onclick"] = jscript;
}
O código começa por verificar se o browser suporta o mecanismo de callback. Para tal recorre à (nova) propriedade SupportsCallback antes de adicionar o código necessário. De referir que a maior parte dos parâmetros definidos podem ser substituídos por eventuais funções. Por exemplo, em vez de passarmos directamente a instrução de javascript que permite obter o valor contido na caixa de texto, podemos indicar uma função.
Definir função cliente de callback
A definição da função de callback no lado cliente permite obter eventuais resultados provenientes do processamento efectuado pelo método situado no lado do servidor. Esta função deve estar de acordo com a seguinte assinatura:
function callbackFunction( result, context )
O primeiro parâmetro contém a informação retornada a partir do processamento do método no lado servidor; por outro lado, o segundo parâmetro contém o valor definido com context pelo método que iniciou o processamento do lado cliente (no caso do sample que acompanha este artigo, esse contexto foi definido como null). Tradicionalmente esta função irá ser utilizada para alterar a informação apresentada ao cliente, como acontece no código que acompanha este artigo. Contudo, nem sempre as coisas correm bem e, devido a isso, também podemos (e devemos) definir uma função que será evocada apenas em caso de erro.
Definir função que processa o erro
Esta função deve ser definida com a mesma assinatura da função anterior e só será evocada se houver uma excepção no processamento da informação no lado servidor.
Conclusões finais
Como é possível verificar, a utilização deste mecanismo não poderia ser mais simples. Ao longo deste artigo vimos quais os passos necessários para utilizarmos o novo mecanismo de callbacks utilizado pela framework 2.0. Para além disso, ainda houve tempo para apresentarmos a arquitectura básica utilizada por este tipo de mecanismo.
Por favor enviem-me as vossas opiniões/sugestões/críticas/correcções para progC@netmadeira.com.
Fiquem bem e boa programação! Até à próxima. O código que acompanha este artigo está disponível na secção dos downloads do site.
Leiam o meu blog em: http://members.netmadeira.com/luisabreu