martes, 29 de marzo de 2011

Script CallBack o “devoluciones de llamada de script”

Este post lo tenía pendiente desde hace mucho porque la característica de Callbacks de cliente en ASP.NET es una característica que yo personalmente no he usado nunca y a decir verdad, creo está en desuso.

En cualquier caso, resulta interesante para completar el circuito de las distintas posibilidades que tenemos en un cliente para ejecutar código en el servidor: ASP.NET AJAX, WebServices y PageMethods, jQuery y ahora… Script Callbacks.

Vaya por delante que no soy ningún experto en Script Callbacks pero voy a intentarlo que es lo principal.

Aunque empiece la casa por el tejado, vamos a ver las principales diferencias que existen entre ASP.NET AJAX (estoy hablando de UpdatePanel), WebServices o PageMethods (que para el caso son lo mismo) y Script Callbacks.

Si agrupamos estos conceptos por su propósito, encontramos los siguientes cometidos:

  • Un UpdatePanel persigue la actualización parcial de una página.
  • Un WebService permite la ejecución de código de servidor desde el cliente y también puede utilizarse para una actualización parcial de la página.
  • Un Script CallBack permite de igual modo, ejecución de código de servidor desde el cliente y también una actualización parcial de la página.

Yo no sé tú, pero yo después de leer lo anterior me he quedado igual… la verdad es que es importante matizar las diferencias.

Un UpdatePanel permite el renderizado de controles de servidor ASP.NET en su interior. Esto es que no hay problema en que la parte de la página que vamos a actualizar incluya controles de servidor ASP.NET. Incluso éstos podrían requerir cierta inicialización de código javascript para crear un componente de cliente asociado. Un ejemplo claro es la colección de controles de ASP.NET AJAX Control Toolkit. A cambio de esta potencia tenemos los siguientes inconvenientes:

Se realiza un postback completo de la página (aunque sea asíncrono y el usuario no perciba el envío del formulario).

Se envía y posteriormente actualiza el ViewState.

Si no se tiene cuidado con el UpdatePanel, podemos llegar a penalizar el rendimiento de nuestra aplicación cuando nuestro principal objetivo era justo el contrario. Mira esta entrada porque podría interesarte.

Se ejecuta todo el ciclo de vida de la página en el servidor (cuidado con código “fijo” – sin verificar IsPostBack o similar - en eventos como Page_Load o Page_PreRender). A mí ahora mismo me sirve especialmente saber que control inició el postback para condicionalmente saltarme ciertas partes del código de eventos como Page_Load o Page_PreRender. Como saber qué control realizó el postback está muy bien explicado aquí

Por otro lado, un WebService es lo más óptimo que podemos utilizar si nuestra intención es ejecutar código en el servidor o devolver HTML sencillo. El envío de datos es mínimo y la respuesta también. Lo importante aquí es comprender que un WebService envía datos simples (texto, json, etc.) y recibe datos simples de nuevo (texto, json, etc.). En este contexto la página .aspx no interviene en el proceso y por ello no hay eventos ni ciclo de vida que valga, es, insisto, lo más óptimo en lo relativo al rendimiento, tanto del servidor como de la red..

Por otro lado, un WebService (o PageMethod) no llega a donde llega el UpdatePanel. Con esto quiero decir que si queremos utilizar un WebService para actualizar el contenido de nuestra página, el resultado devuelto va a ser siempre texto y entonces controles que requieran inicialización en el cliente a través de javascript serán inviables de devolver. De hecho, lo más “especial” que se puede hacer en un WebService en lo relativo a la actualización de la página es renderizar un control en un Stream que después devolveremos al cliente, es decir, devolvemos el resultado de renderizar, pero cualquier otra operación de registro necesaria en cliente llevada a cabo por el control no será ejecutada en nuestra página, y a día de hoy y con la cantidad de controles de servidor ASP.NET que requieren de javascript asociado, comienza a no ser una alternativa viable… Hombre! Para devolver la hora sí, pero poco más… aunque no sé yo porque jQuery y jQuery UI hace maravillas… pero esa es otro historia!

De todas formas, si quieres saber más de servicios web visita estas entradas.

Para renderizar un control de servidor en un servicio web la idea es crear una nueva página, Dim page As New Page, después carga el control con page.LoadControl y después ejecutar la página pero devolver el resultado a una stream que será el texto que finalmente devolvamos en el servicio web. Todo esto lo puedes encontrar en los siguientes enlaces:

http://stackoverflow.com/questions/976524/issues-rendering-usercontrol-using-server-execute-in-an-asmx-web-service

http://encosia.com/2008/02/05/boost-aspnet-performance-with-deferred-content-loading/

http://weblogs.asp.net/sanjeevagarwal/archive/2008/07/22/Dynamically-create-ASP.NET-user-control-using-ASP.NET-Ajax-and-Web-Service.aspx

Mi conclusión final es que depende del tipo de control de servidor ASP.NET que quieras devolver un servicio web te puede o no valer.

Como decía al comienzo del post, a día de hoy yo me muevo con estas soluciones pero últimamente (y porque estoy utilizando la suite de controles de Telerik) estoy viendo que existe una tercera opción que es Script Callbacks.

A primera vista es una solución intermedia que no sé si hoy sigue siendo válida (porque creo que Script Callback fue la primera aproximación que ASP.NET dio al problema de ejecutar código en el servidor) pero aun así tiene sus puntos fuertes que aún hoy lo hacen especial.

Un Script Callback permite llamar a un método de una página desde cliente y lo más importante es que este método no tiene que ser Shared (como es requisito para un PageMethod).

Un Script Callback lanza el ciclo de vida de la página… pero no todo (la verdad es que es de locos!). Los eventos Page_PreRender, Page_PreRenderComplete y Page_SaveStateComplete no se ejecutan, así que también podemos decir que un Script CallBack no actualiza el ViewState (aunque sí lo envía y carga en la página para que funcionen correctamente los eventos Init y Load).

Un Script Callback finalmente se parece mucho a un WebService en que simplemente recibe texto a través de una función de javascript, y es responsabilidad del programador actualizar la página o hacer lo que crea oportuno con este resultado. Esto indica que el anterior problema expuesto para un WebService y un control de servidor ASP.NET que utilice AJAX sigue vigente para Script CallBack.

Después de esto, no se a ti, pero a mi… ni frio ni calor (quizás poder ejecutar un método de instancia de la página sea algo positivo, pero poco más…)

Sin más, vamos a ver un ejemplo de Script Callback y que sé tú mismo quién decida si te resulta o no de utilidad.

Primero el código de servidor:

Partial Class _Default

    Inherits System.Web.UI.Page

    Implements ICallbackEventHandler

 

    Private fecha As String

 

    Public Function GetCallbackResult() As String Implements System.Web.UI.ICallbackEventHandler.GetCallbackResult

        ' Método encargado de devolver el resultado como texto obtenido por la ejecución del método RaiseCallbackEvent.

        Return fecha

    End Function

 

    Public Sub RaiseCallbackEvent(eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

        ' Método que será llamado desde cliente.

        ' Método que se encarga de realizar todas las operaciones oportunas.

        fecha = System.DateTime.Now.ToString

    End Sub

 

    Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load

        ' Este código conecta el cliente con el servidor.

 

        ' WebForm_DoCallback('__Page',arg,retornoLlamadaAsincrona,context,null,false)

        Dim referencia As String = ClientScript.GetCallbackEventReference(Me, "arg", "retornoLlamadaAsincrona", "context")

 

        ' function llamadaAsincrona(arg,context){WebForm_DoCallback('__Page', arg,arrancarLlamadaAsincrona,context,null,false);}

        Dim llamada As String = "function llamadaAsincrona(arg, context)" & "{" & referencia & ";}"

 

        Page.ClientScript.RegisterClientScriptBlock(Me.GetType(), "key_1", llamada, True)

    End Sub

End Class

 

Y ahora el código de cliente:

    <div>

        <input type="button" value="Llamar" onclick="arrancarLlamadaAsincrona();" />

        <asp:Label ID="lblFecha" runat="server"></asp:Label>

    </div>

    <script type="text/javascript">

        function arrancarLlamadaAsincrona() {

            /*

El parámetro "argumento" será recibido en el servidor en el evento

Public Sub RaiseCallbackEvent(eventArgument As String) Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

*/

 

            /*

Por otro lado el parámetro "contexto" será devuelto en la función de callback de javascript como segundo parámetro */

            llamadaAsincrona("argumento", "contexto");

        }

 

        function retornoLlamadaAsincrona(fecha, contexto) {

            $get("<%=lblFecha.ClientID %>").innerText = fecha;

        }

    </script>

 

A mí personalmente me parece una “jartá” de código… pero bueno, ahí está.

 

Un saludo!

No hay comentarios:

Publicar un comentario