ASP.NET AJAX: posibilidades y escenarios de uso

La herramienta elegida por la mayoría de desarrolladores ASP.NET para implementar funcionalidades AJAX es ASP.NET AJAX. Encuestas como la de Nitobi sirven para dar una idea de lo que están usando los desarrolladores en general para implementar AJAX. En el mundo .NET en particular podemos ver en esta otra encuesta que el 73% de los desarrolladores usa el framework oficial de Microsoft para AJAX.

A colación de esto, resulta que recientemente (el mes pasado) Microsoft ha anunciado que va a incluir en Visual Studio soporte para JQuery, con lo que se amplian las opciones oficiales con la inclusión de esta librería. Sobre ASP.NET AJAX y JQuery os recomiendo este evento que organiza Second NUG y que tendrá lugar el 4 de Noviembre. Volviendo a las encuestas mencionadas, puede que éstas no tengan el mayor rigor científico, pero nos sirven para hacernos una idea.

 

 

 

En la encuesta de Nitobi la gran mayoría departicipantes respondieron que la razón por la que usan AJAX es para mejorar la experiencia de usuario. Es aquí donde ASP.NET AJAX cumple su función a la perfección, presentando una interfaz rica típica de AJAX, eliminando los refrescos completos de las páginas, el flickering, haciendo que la interfaz proporcione más feedback al usuario y sea más interactiva. En definitiva, mejorando la experiencia de usuario como se espera de AJAX.

Sin embargo, y pese a ser usado por la mayoría de la comunidad, a veces existe desconocimiento sobre su arquitectura, que usándola de la manera típica con ScriptManager y UpdatePanels difiere de lo que es la esencia de AJAX. En este post hablaremos sobre este modelo, sobre la alternativa más purista que provee el propio ASP.NET y sobrequé escenarios serían más propicios para una cosa u otra.

 

Partial Rendering

ASP.NET AJAX usa el modelo de Partial Rendering, también llamado AJAX Postback, que se basa precisamente en eso: un postback normal de ASP.NET llevado a cabo por el framework de AJAX y que, por tanto, respeta todo el ciclo de vida de una página ASP.NET. El runtime no hace nada diferente si ha recibido una petición desde un postback clásico o desde un postback AJAX hasta la etapa de renderizado. Durante el procesamiento de la petición se disparan todos los eventos a los que un desarrollador de ASP.NET está acostumbrado:

 

 Ciclo de vida de una página ASP.NET

 

Parte fundamental de esta arquitectura es el ScriptManager. Debe existir una instancia de este control en toda página ASP.NET que quiera implementar AJAX y al menos un UpdatePanel que envuelva la región de la página que se quiere refrescar.

En el lado del cliente, el ScriptManager es el encargado de averiguar si existen UpdatePanels, en cuyo caso envía las peticiones al servidor a través del objeto XMLHttpRequest del browser, no a través de un postback normal. En la petición se incluye toda la información de un postback típico más alguna información que el propio ScriptManager necesita del lado del servidor para procesar un postback AJAX. Aquí se ve que como en realidad no se está enviando menos información al servidor, como podríamos pensar al refrescarse sólo fragmentos de la página, y esto ya nos va dando información para plantearnos qué usos pueden ser indicados para este modelo de AJAX.

En el lado del servdidor el ScriptManager averigua si el postback que se está procesando es un postback AJAX o un postback normal. Para esto, inspecciona las cabeceras HTTP de la petición en busca de la información extra que le indica qué tipo de postback se ha producido. Una vez identificado como postback AJAX, invoca al método de renderizado ad hoc para AJAX en el que se buscan todos los UpdatePanels de la página, se renderiza el HTML para su contenido y se manda de vuelta al cliente (junto con alguna otra información). 

Aquí es donde viene el ahorro, ya que sólo se envía de vuelta al cliente el HTML de los UpdatePanels que se han actualizado. Entra en juego también aquí un buen diseño, ya que el ahorro puede ser mínimo si usamos la técnica a veces abusada de poner un UpdatePanel que englobe practicamente todo el contenido de la página o podemos mejorar significativamente el peso de la respuesta si identificamos exactamente las regiones que necesitan actualizarse y dejamos fuera HTML estático que no necesita viajar de vuelta. Una buena práctica es usar los UpdatePanels que sean necesarios para encapsular sólo las regiones que van a ser actualizadas en un determinado postback AJAX, así como jugar con la propiedad UpdateMode (establecerla a Conditional) de los UpdatePanels para evitar que siempre que haya un postback se refresquen todos, lo interesante es que cada uno se refresque refresque cuando el postback haya sido disparado por un control especifico que se encargue de su refresco.

De cualquier manera, siempre hay un ahorro en la respuesta, aunque no se apliquen estas buenas prácticas, pero si se aplican el ahorro será más importante. En global, contando con el peso extra de la petición y el ahorro de la respuesta, un postback AJAX ahorra ancho de banda respecto a un postback normal.

Hasta aquí Partial Rendering, como vemos, este patrón no tiene nada que ver con las llamadas asíncronas puras que se cuentan de AJAX, en la que se monta una petición sobre XMLHttpRequest, se ejecuta cierto servicio en el servidor y después se actualiza el HTML de la página con Javascript a través del DOM con la información de retorno del servicio.

 

Script Services y Page Methods

La alternativa existe, ya que en ASP.NET también podemos decidir saltarnos este modelo de Partial Rendering y decantarnos por una arquitectura puramente AJAX usando Script Services o Page Methods.

No los llamamos servicios web porque su ámbito de uso y su finalidad son bien diferentes: estos servicios nacen sólo para exponer datos concretos que satisfagan una llamada asíncrona desde el cliente, no como parte lógica de una arquitectura SOA más compleja. Aparte de esto, este intercambio se hace a través de HTTP y en formatos simples como JSON o XML, sin incluir información adicional de protocolos más pesados como SOAP ni metadatos que describan el servicio en forma de WSDL. Este tipo de servicios son conocidos como REST (Representational State Transfer) y nacen para satisfacer las necesidades de llamadas asíncronas AJAX, por lo que son más ligeros y menos ambiciosos que los servicios web.

Como hemos dicho, estos servicios son accedidos a través de HTTP, por lo que el formato de intercambio elegido puede ser desde JSON, XML o incluso texto plano. JSON (JavaScript Object Notation) suele ser el preferido porque aporta la ventaja de que codifica los datos en forma clave-valor y la función eval() de JavaScript es capaz de decodificar un string JSON en una colección de claves-valor. JSON introduce también algunas nuevas superficies de ataque para las páginas web, pero de eso habría que hablar en otro post.

Resumiendo lo que hemos dicho hasta ahora, una arquitectura AJAX basada en llamadas asíncronas puras consiste en exponer una capa de servicios REST que se encuentra encima de las capas del back-end de nuestra aplicación (típicamente la capa de negocio) y hacer llamadas a los mismos desde el browser cliente a través de JavaScript y XMLHttpRequest. Una vez que se recibe la respuesta hay que actualizar el fragmento de HTML de la interfaz que se iba a refrescar haciendo uso de un front-end construido en JavaScript y el DOM.

 

 

 

En ASP.NET existen dos maneras de seguir esta arquitectura. Una es a través de una modalidad especial de servicios web decorados con el atributo [ScriptService] y otra es a través de Page Methods. Los Page Methods son métodos estáticos que se crean en una página ASPX concreta y sólo pueden ser invocados desde dentro de esa misma página. Para marcar un método como Page Method hay que decorarlo con el atributo [Webmethod] y establecer EnablePageMethods=»true» en el ScriptManager.

Por su parte, los servicios web decorados con [ScripService] pueden ser llamados desde un script de una página cliente debido precisamente al atributo [ScriptService], cualquier método de un servicio web tradicional no podría ser invocado, el pipeline de ASP.NET devolvería un error. Este servicio también podría ser invocado a través de SOAP, pero esto se puede evitar especificándolo en el web.config. Esto último sería lo conveniente, ya que como dijimos antes, este tipo de servicios sólo debería tener sentido para proporcionarnos una determinada funcionalidad en un contexto AJAX. Por otro lado, el formato en el que se devuelve la información puede ser establecido dentro del atributo ScriptMethod (por defecto es JSON).

La cosa se complica más a la hora de recibir los datos en el lado del cliente, ya que es trabajo del desarrollador el recuperar toda la información de manera adecuada y rehacer el HTML necesario para actualizar la página. Por ejemplo, si es necesario pasar objetos desde el back-end hasta el cliente, luego habrá que recuperar los objetos codificados en JSON y guardarlos en sus objetos JavaScript espejo correspondientes en el cliente, por lo que se puede acumular bastante código JavaScript encargado de todas estas tareas. Otra cuestión que queda en nuestras manos es actualizar la interfaz de usuario. Podemos hacernos una idea de interfaces de usuario que no sean triviales de actualizar, como puede ser paginar un grid que muestre un listado. El parseo de la información de los objetos JavaScript y la creación, relleno y formateo del grid puede convertirse en una tarea árida, incluso con la ayuda de librerías JavaScript que faciliten la tarea. Otra cuestión que no hay que olvidar es la cantidad de código que se está escribiendo en un lenguaje interpretado como JavaScript, con la consecuente penalización de rendimiento y el hecho de que cada navegador proporciona su propio motor de JavaScript, con sus propios errores y rendimientos.

 

Conclusiones: escenarios de uso

Como hemos visto, ASP.NET AJAX con Partial Rendering es un patrón que presenta una ventaja clara: no se modifica en absoluto el modelo de desarrollo ASP.NET, lo cual es muy bueno para los equipos de desarrollo que, con poco esfuerzo pueden añadir funcionalidades AJAX a las aplicaciones web. Es un modelo poco intrusivo que exige sólo las destrezas propias del uso de la herramienta y las nuevas funcionalidades, pero no plantea un cambio de paradigma de desarrollo. Por otro lado, de cara al usuario, se consigue el fin de proporciona una interfaz rica típicamente AJAX.

Sus inconvenientes vienen precisamente derivados de no deshacerse del modelo de desarrollo de ASP.NET, ya que no se saca todo el partido posible al ahorro de ancho de banda de las peticiones, sino que se sigue haciendo un postback con mucha información al servidor.

Por su parte, los Script Services tienen su fuerte donde falla Partial Rendering, ya que sólo viaja al servidor una petición http para un Script Service y el servidor sólo devuelve la información necesaria para actualizar el fragmento de página que se quiere refrescar, sin postback. El ahorro de ancho de banda es máximo con una arquitectura basada en Script Services y la experiencia de usuario sigue obteniendo los mismos beneficios que con Partial Rendering. 

Será cada arquitecto quien tendrá que decidir qué se ajusta mejor para su escenario concreto: máximo rendimiento o máxima facilidad de desarrollo, considerando también la exigencia a nivel desarrollo con JavaScript que plantean cada altenativa.

Como normal general, la mayor parte de los escenarios que nos vamos a encontrar en las aplicaciones de gestión, son idóneas para usar Partial Rendering, ya que el rendimiento no es un factor clave y sí lo es el poder crear y gestionar lógica compleja que de solución a casos de uso que exigen código complejo del lado del servidor. Este tipo de aplicaciones no se van a beneficiar tanto de algún segundo menos en tiempo de respuesta como sí van a estar penalizadas por un incremento de la dificultad y el tiempo de desarrollo que implica tener que gestionar la interfaz de usuario y los datos que llegan al navegador de casos de uso complejos a base de JavaScript, aparte de lo inmantenible que puede tornarse este código a medio plazo.

Si cierto caso de uso produce páginas demasiado pesadas que necesitan optimización, una buena idea sería optar por rediseñar el caso de uso para crear interfaces más ligeras, separando funcionalidades que antes estaban juntas y poniéndolas en diferentes páginas, etc. Si por cuestiones de requisitos esto es inviable (todo puede ocurrir) podemos ir a un modelo híbrido, en el que el grueso de nuestra aplicación esté desarrollada con Partial Rendering y usar Script Services para los puntos que necesiten máximo rendimiento.

Arrancamos…

Hoy empiezo este blog y comienzo saludando a toda la comunidad y agradeciendo a la gente de Plain Concepts y Rodrigo Corral el hueco que me han hecho como vecino de Geeks.ms. El señor Toni Recio también tiene que ver en este asunto 🙂


Desde aquí escibiré sobre aspectos relacionados con el desarrollo de software, con temas encuadrados en cualquiera de sus etapas, e intentaré aportar mi granito de arena a la comunidad con experiencias y artículos que espero que resulten de provecho. 


Nos vemos en la próxima entrada metiéndonos ya en harina 🙂


¡Saludos!