Desenvolvimento - ASP. NET

Client-Side Callbacks - ASP.NET 2.0

Um dos principais desafios dos desenvolvedores de aplicações Web é construir uma aplicação intuitiva e ao mesmo tempo dinâmica, tornando a aplicação o mais próximo possível do "mundo Windows". Conheça mais neste artigo.

por Israel Aéce



function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i Faça o download do código.

Um dos principais desafios dos desenvolvedores de aplicações Web é construir uma aplicação intuitiva e ao mesmo tempo dinâmica, tornando a aplicação o mais próximo possível do "mundo Windows". Como essa aproximação é sempre comparada pelos clientes, o principal ponto negativo que as pessoas que estão migrando suas aplicações Windows para aplicações Web encontram é o fato de quando submetemos algo para ser processado no servidor, a página causa um Refresh, ou seja, ela é totalmente reconstruída.

Com esse comportamento as aplicações Web foram bastante crucificadas quando comparadas as aplicações Windows tradicionais. Com a necessidade de ter algo ainda mais dinâmico, algumas tecnologias foram criadas em paralelo as de desenvolvimento dinâmico de sites, tornando as aplicações Web ainda mais interativas. Uma das primeiras tecnologias que apareceram para preencher essa lacuna foi o Remote Scripting, que fornece uma infraestrutura para invocar método server-side, ou seja, que rodam no servidor sem a necessidade de submeter a página. Essa tecnologia utiliza Java Applets para fazer a comunicação com o servidor, comunicando através de protocolo HTTP. Há também opções onde não são necessários Applets para isso, utilizando apenas JavaScript e DHTML, como podemos ver neste componente.

Atualmente, se fala bastante em uma tecnologia chamada AJAX (Asynchronous JavaScript and XML), a qual tem a mesma finalidade da anterior e utiliza o objeto XMLHTTPRequest para a comunicação entre o browser cliente e o servidor. Na versão ASP.NET 2.0, a Microsoft implementou o que chamamos de Client-Side Callbacks. Essa é uma opção paleativa que a Microsoft introduziu no ASP.NET 2.0; paleativa porque a própria Microsoft trabalha atualmente em um projeto chamado Atlas, qual brevemente estará disponível. Esse projeto trará aos desenvolvedores ASP.NET uma facilidade enorme no desenvolvimento, encapsulando boa parte do código Javascript que é requerido nestes cenários. Inclusive serão fornecidos diversos controles Drag & Drop, permitindo assim a codificação declarativamente.

Semelhante ao Remote Scripting, os Client-Side Callbacks permitem chamar um código qualquer VB.NET ou C#, que corre no servidor, através do cliente, sem a necessidade de atualizar a página. A única diferença é que ele utiliza XmlHTTP para se comunicar com o servidor ao invés de Java Applet.

O processo é bastante simples: um determinado controle, do lado do cliente (browser), efetua um pedido, de forma assíncrona, a uma função do servidor através de uma função Javascript que é automaticamente embutida (esta é chamada de WebForm_DoCallback) na página pelo runtime do ASP.NET, e esta por sua vez é responsável por criar e configurar a estrutura necessária para enviar o pedido para o servidor. Depois que a função (embutida pelo ASP.NET) é executada, resta saber como invocar o método Javascript quando a resposta do servidor for retornada (callback). Eis o momento que entra em cena a Interface ICallbackEventHandler.

Esta Interface contém somente dois métodos: RaiseCallbackEvent(string) e GetCallbackResult(). Durante o processo de callback o ciclo de vida da página é um pouco diferente da normalidade, onde todos os eventos serão executados normalmente até o evento PreRender; como esse evento é responsável pela geração do código HTML da página, ele deve ser suprimido. Ao invés de retornar o HTML gerado pelo evento PreRender, é devolvido apenas o retorno do processamento do método GetCallbackResult(). O método RaiseCallbackEvent é invocado anteriormente, depois da conclusão do evento LoadComplete, onde podemos fazer inicializações de objetos, entre outras coisas necessárias para atender à este pedido. O porque de termos dois métodos nesta Interface é justamente para permitir o processamento assíncrono do pedido. Para termos uma idéia do fluxo de processamento de uma página com callback e sem callback, basta analisar a imagem abaixo:

Figura 1 - Fluxo de páginas com e sem callbacks.


Para vermos o funcionamento em um ambiente "quase real", devemos recuperar o nome da pessoa dado um número de C.P.F., ele irá fazer uma pesquisa e, se encontrar alguma pessoa com este código, o nome da mesma será retornado. Claro que isso não reflete exatamente o mundo real, já que nestes casos o ideal seria fazer uma consulta em alguma base de dados. O código abaixo mostra a implementação da Interface ICallbackEventHandler no CodeBehind de uma página ASPX.

public partial class Cadastro : System.Web.UI.Page, ICallbackEventHandler
{
    private string _cpf;
    
    public void RaiseCallbackEvent(string eventArgs)
    {
        this._cpf = eventArgs;
    }

    public string GetCallbackResult()
    {
        if(this._cpf.Length != 11)
            throw new ArgumentException("Comprimento do CPF incorreto.");

        if(this._cpf == "00011122233")
            return "Israel Aece";
        else if(this._cpf == "00011122244")
            return "Juliano Aece";
        else if(this._cpf == "00011122255")
            return "Claudia Fernanda";

        throw new ArgumentException("Registro inexistente.");
    }
}
Public Partial Class Cadastro
    Inherits System.Web.UI.Page
    Implements ICallbackEventHandler

    Private _cpf As String

    Public Sub RaiseCallbackEvent(eventArgs As String)
        Implements ICallbackEventHandler.RaiseCallbackEvent

        Me._cpf = eventArgs
    End Sub

    Public Function GetCallbackResult() As String
        Implements ICallbackEventHandler.GetCallbackResult

        If Not Me._cpf.Length = 11 Then
            Throw New ArgumentException("Comprimento do CPF incorreto.")
        End If

        If Me._cpf = "00011122233"
            Return "Israel Aece"
        ElseIf Me._cpf = "00011122244"
            Return "Juliano Aece"
        ElseIf Me._cpf = "00011122255"
            Return "Claudia Fernanda"
        End If

        Throw New ArgumentException("Registro inexistente.")
    End Function
End Class
C# VB.NET

Como podemos ver no código acima, o CodeBehind da nossa página já está preparado para trabalhar com o processo de callback. Mas somente isso não é necessário: devemos lembrar que o cliente (browser) é responsável por disparar o processo e, quando a função de callback retornar, também deverá receber o resultado, manipulá-lo e, conseqüentemente, apresentar ao usuário. A função que é responsável por disparar o ínicio do processo é chamada de WebForm_DoCallback, pois, como já vimos anteriormente, ela é embutida automaticamente pelo ASP.NET e a única coisa que temos que nos preocupar é quem (controle) e quando (evento) vai disparar esse processo. Antes de apresentar o código responsável pela geração da função que será colocada no cliente para disparar o callback, é importante conhecer duas novas propriedades introduzidas no ASP.NET 2.0, para manipularmos os callbakcs. A primeira delas, a propriedade SupportsCallback, que está contida dentro de Request.Browser, que retorna um valor verdadeiro ou falso indicando se o browser do cliente suporta ou não os callbacks. Já a segunda é a propriedade IsCallback, que foi adicionada na classe Page que, assim como a propriedade IsPostBack, retorna um valor verdadeiro ou falso indicando se é ou não uma requisição resultante de um processo de callback.

Agora precisamos saber como atribuir a função WebForm_DoCallback em algum evento de um determinado controle no CodeBehind. A classe Page, através de uma nova propriedade chamada ClientScript, fornece um objeto do tipo ClientScriptManager, que este por sua vez, disponibiliza ao desenvolvedor um método chamado GetCallbackEventReference. Este método, dados seus parâmetros (os quais veremos a seguir), retorna uma String que refere-se à uma função client-side que será disparada através de algum evento (também no cliente), que dará início ao processo de callback. Para ilustrar o cenário que descrevemos acima, teremos o seguinte formulário:

Figura 2 - Formulário para os testes de callback.


Como prometido, vamos analisar detalhadamente a função GetCallbackEventReference. A assinatura do mesmo é a seguinte:

public string GetCallbackEventReference (
	string target,
	string argument,
	string clientCallback,
	string context,
	string clientErrorCallback,
	bool useAsync
)
Public Function GetCallbackEventReference ( _
	target As String, _
	argument As String, _
	clientCallback As String, _
	context As String, _
	clientErrorCallback As String, _
	useAsync As Boolean _
) As String
C# VB.NET

O primeiro parâmetro, target, é o nome do controle de servidor que tratará o callback e o mesmo deve implementar a Interface ICallbackEventHandler. O segundo parâmetro, argument, é um argumento passado do lado cliente para o método do servidor RaiseCallbackEvent, proveniente da Interface ICallbackEventHandler. O terceiro parâmetro, clientCallback, é o nome da função cliente (Javascript) que será disparada quando o callback for processado e retornado para o cliente. Este método tem a responsabilidade de tratar e exibir o resultado. O parâmetro context é um valor que definimos e não é enviado ao servidor, mas é um valor que é passado a todos os métodos cliente. Como algum erro no processamento pode acontecer e uma Exception pode ser lançada, definimos através do parâmetro clientErrorCallback uma função que está também no cliente, que será responsável por tratar algum eventual erro. Finalmente o último dos parâmetros: useAsync; definimos como True se quisermos executar o processo de forma assíncrona e False se quisermos executá-lo de forma síncrona. Agora que já sabemos quais são as funções de cada um dos parâmetros, vejamos como fica o evento Load do WebForm:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsCallback && Request.Browser.SupportsCallback)
    {
        string funcaoJS = this.ClientScript.GetCallbackEventReference(
            this,
            "document.getElementById("" + this.txtCPF.ClientID + "").value",
            "ExibeNome",
            ""Aqui é o contexto."",
            "TrataErro",
            true);

        this.btnBuscar.OnClientClick = string.Concat(funcaoJS, "; return false;");
    }
}
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles Me.Load

    If Not Page.IsCallback AndAlso Request.Browser.SupportsCallback Then
        Dim funcaoJS As String = Me.ClientScript.GetCallbackEventReference( _
            Me, _
            "document.getElementById("" + Me.txtCPF.ClientID + "").value", _
            "ExibeNome", _
            ""Aqui é o contexto."", _
            "TrataErro", _
            True)

        Me.btnBuscar.OnClientClick = String.Concat(funcaoJS, "; return false;")
    End If
End Sub
C# VB.NET

Com isso, só falta agora no cliente definirmos a função que tratará o retorno do callback e a função que será disparada caso algum Exception ocorrer. O código cliente fica semelhante ao que vemos abaixo:

<script language="javascript">
    function ExibeNome(arg, context)
    {
        document.getElementById("txtNome").value = arg;
    }
    
    function TrataErro(arg, context)
    {
        alert(arg);
        alert("Contexto: " + context);
    }
</script>
Javascript

Como podemos ver na figura 3, logo abaixo, o método WebForm_DoCallback foi atribuído no evento onClick do Button, já configurado com os devidos parâmetros que definimos no codebehind através do método GetCallbackEventReference. Para quem não percebeu como o método apareceu no evento onClick do Button, o retorno da função GetCallbackEventReference é atribuído à propriedade OnClientClick do Button que será responsável por iniciar o callback, ou melhor, será responsável por buscar o nome dado um C.P.F..

Figura 3 - Código definido no cliente.


Depois de todas essas configurações realizadas, podemos agora executar algum código no servidor sem a necessidade de atualizar a página. A responsável pela atualização dos dados retornados do callback é uma função Javascript que estará no cliente. É importante dizer que o controle GridView também fornece as funcionalidades de ordenação e paginação dos registros, sem a necessidade de atualizar a página toda. É muito simples colocar em funcionamento essa técnica, bastando apenas marcar a propriedade EnableSortingAndPagingCallbacks como True e a paginação e ordenação do controle GridView passa a ser feita sem a atualização da página como um todo.

CONCLUSÃO: Como pudemos ver no decorrer deste artigo, a Microsoft facilitou o desenvolvimento para trabalhar com o processamento no servidor sem a necessidade de atualizarmos/reconstruirmos a página toda, evitando que se recorra a componentes de terceiros para essa necessidade. Mas vale lembrar que a escrita de código Javascript ainda é necessária, já que depois que o callback é retornado, há necessidade de atualizar partes da página para exibir o resultado ao cliente. Para finalizar, é importante ratificar que a Microsoft está trabalhando em um projeto, chamado Atlas, que permitirá e deixará a codificação mais flexível para esse tipo de utilização.
Israel Aéce

Israel Aéce - Especialista em tecnologias de desenvolvimento Microsoft, atua como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. Como instrutor Microsoft, leciona sobre o desenvolvimento de aplicações .NET. É palestrante em diversos eventos Microsoft no Brasil e autor de diversos artigos que podem ser lidos a partir de seu site http://www.israelaece.com/. Possui as seguintes credenciais: MVP (Connected System Developer), MCP, MCAD, MCTS (Web, Windows, Distributed, ASP.NET 3.5, ADO.NET 3.5, Windows Forms 3.5 e WCF), MCPD (Web, Windows, Enterprise, ASP.NET 3.5 e Windows 3.5) e MCT.