Aumenta tu productividad al desarrollar aplicaciones Web 2.0. Este es el tema central del libro y con él conseguirás aprender todo lo necesario para c...
El tema de la comunicación está en el centro de todos los debates, porque su carácter masivo y creciente configura la realidad e incide en todas las alternativas. En una primera parte, el…Descripción completa
Monografia Adicciones Desde La Logoterapia
Monografia Adicciones Desde La Logoterapia
Estudio urbano de la ciudad latinoamericana desde su morfologia de crecimiento, como influye la morfologia en la concepcion del hecho urbano
Libro catolicoDescripción completa
Descripción completa
Viata Incepe La 40
Mirada, psicoanálisis
Descripción: Competencias de Perrenoud
Descripción: Economía
Descripción completa
Descripción completa
Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0) José Manuel Alarcón Aguín
Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0) No está permitida la reproducción total o parcial de este libro, ni su tratamiento informático,
ni la transmisión de ninguna forma o por cualquier medio, ya sea electrónico, mecánico, por fotocopia, por registro u otros métodos, sin el permiso previo y por escrito de los titulares del Copyright. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.
ISBN: 978-84-936696-1-4 Depósito Legal: VG 961-2009 Impreso en España-Printed in Spain
Agradecimientos La tarea de escribir un libro nunca es fácil, ni para el autor ni para quienes conviven o trabajan con él. Por eso, entre otras razones, un libro nunca es exclusivamente obra del que lo escribe. Así, debo agradecer como siempre a mi familia el que hayan aguantado mis contestaciones secas al teléfono cuando interrumpían la escritura. Eso y que no me haya pasado a verlos en unas cuantas semanas, claro. En Krasis, si ya suele ser difícil hablar conmigo, mientras estaba con el libro ha sido poco menos que imposible. Por ello vaya mi agradecimiento también por su paciencia a Héctor, María, Pablo, Verónica, Fran, Yazmín, Dani y Eduardo. A Pablo Iglesias hay que agradecerle especialmente su trabajo con las cubiertas del libro. ¡Preciosas! El bueno de Octavio Hernández, un sabio de la computación y autor también de Krasis Press, revisó parte del material del libro desde la soleada California. Gracias maestro. La gente de Microsoft Ibérica, y en especial en este caso Beatriz y David, que siempre se acuerdan de mi cuando hay algo de ASP.NET de por medio :-) Finalmente, como siempre, a la principal sufridora de mis delirios frikis, Eva. Te ρ = 1 - sin(θ)
vii
Contenido CONTENIDO..........................................................................................................................ix Presentación......................................................................................................................xiii 1. ASP.NET y sus versiones..................................................................................1 1.- La historia de ASP.NET hasta su versión 2.0........................................................1 2.- La versión 3.0 de .NET.................................................................................................3 3.- La versión 3.5 de la plataforma..................................................................................4 4.- Un Service Pack que es mucho más que un parche...........................................5 5.- .NET 4.0 y Visual Studio 2010....................................................................................6 6.- De qué trata este libro (y qué deja fuera).............................................................7 7.- En resumen........................................................................................................................8 2. FUNDAMENTOS DE AJAX......................................................................................9 1.- Interfaces de usuario avanzadas................................................................................10 2.- Un poco de teoría: el objeto XMLHttpRequest.................................................11 3.- Basta de teoría: vamos a la práctica.......................................................................13 4.- Problemas típicos de Ajax y sus soluciones.........................................................16 4.1.- Llamadas fuera de dominio..............................................................................16 4.2.- Gestión de errores y llamadas que no vuelven........................................17 4.3.- Envío de datos al servidor...............................................................................18 4.4.- Contenidos no actualizados debido a cachés........................................... 20 5.- Devolución de información: JSON...........................................................................21 6.- En resumen..................................................................................................................... 23 3. ASP.NET AJAX EN EL SERVIDOR.................................................................... 25 1.- Un primer ejemplo: mejora de una aplicación básica con AJAX.................. 26 2.- Postbacks parciales y repintados parciales de página....................................... 30 3.- El control ScriptManager............................................................................................31 4.- El control UpdatePanel............................................................................................... 32 5.- Modos de actualización parcial................................................................................ 33 6.- Disparadores.................................................................................................................. 34 7.- Indicación de progreso de las llamadas asíncronas............................................ 37 8.- Refrescos parciales periódicos................................................................................. 40 9.- Unicidad del ScriptManager....................................................................................... 42 10.- El control ScriptManagerProxy.............................................................................. 43 11.- Gestión de errores AJAX........................................................................................ 43 12.- Incompatibilidades de AJAX................................................................................... 48 13.- AJAX Control Toolkit.............................................................................................. 50 4. ASP.NET AJAX EN EL NAVEGADOR............................................................ 59 1.- Retrollamadas de red a métodos estáticos.......................................................... 60 ix
x Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
2.-
3.4.5.6.-
1.1.- Análisis de tráfico con un UpdatePanel.......................................................61 1.2.- Ejemplo optimizado con retrollamadas de red a métodos estáticos................................................................................................................ 63 Llamadas a servicios Web..........................................................................................67 2.1.- Definición de un servicio Web ASMX para ser llamado desde JavaScript.............................................................................................................. 68 2.2- Creación de la página cliente para llamar al servicio..............................71 Servicios de aplicación: Membership y Roles desde el navegador................ 75 Referencias a scripts en páginas y controles........................................................81 Optimización de uso de bibliotecas en ASP.NET 4.0....................................... 84 5.1.- Combinación de scripts.................................................................................... 87 En resumen..................................................................................................................... 88
5. ENLAZADO A DATOS EN EL NAVEGADOR........................................... 89 1.- Concepto de plantillas de lado cliente...................................................................91 2.- Las bases para trabajar............................................................................................... 93 3.- Definición de la plantilla de productos................................................................. 94 4.- La clase DataView........................................................................................................ 95 5.- Pseudo-columnas y atributos especiales............................................................... 98 6.- Atributos sys condicionales..................................................................................... 100 7.- Atributos code para renderizado condicional.................................................... 101 8.- Enlazado de datos en tiempo real........................................................................ 103 9.- Vistas maestro-detalle: preparar el maestro..................................................... 105 10.- Vistas maestrro-detalle: preparar los detalles................................................ 106 11.- Devolver los datos modificados al servidor: contextos de datos............. 108 12.- La definición del método de guardado de cambios.......................................110 13.- Historia del navegador.............................................................................................112 14.- En resumen..................................................................................................................115 6. ASP.NET DYNAMIC DATA: INTERFACES DE DATOS A LA VELOCIDAD DE LA LUZ.......................................................................................117 1.- ¿Qué es Dynamic Data?............................................................................................118 2.- Nuestro primer proyecto con Dynamic Data...................................................119 3.- Definir el modelo de datos......................................................................................121 4.- Añadiendo el modelo a Dynamic Data............................................................... 122 5.- Plantillas de interfaz de usuario............................................................................. 124 5.1.- Diseccionando una plantilla........................................................................... 125 6.- Plantillas para entidades........................................................................................... 129 7.- Plantillas para campos............................................................................................... 129 8.- Las rutas de las páginas dinámicas....................................................................... 132 8.1.- Parámetros individuales en las rutas.......................................................... 135 9.- Ampliando los metadatos del modelo................................................................. 136 9.1.- Mejorando la validación de campos............................................................ 138 9.2.- Validaciones personalizadas........................................................................... 140 10.- Plantillas de campos propias..................................................................................141 11.- Dynamic Data en páginas propias....................................................................... 143 12.- En resumen................................................................................................................ 146
Contenido xi
7. FILTRADO DE DATOS AUTOMÁTICO CON QUERYEXTENDER................................................................................................... 147 1.- El control QueryExtender....................................................................................... 147 2.- Tipos de filtros............................................................................................................ 148 3.- Creación de la página base para ejemplo........................................................... 149 4.- Primer filtro: búsqueda por nombre.................................................................... 152 5.- Filtrado por rangos de valores.............................................................................. 154 6.- Filtrado por valores de propiedades.................................................................... 154 7.- Parámetros de filtrado.............................................................................................. 155 8.- En resumen................................................................................................................... 157 ÍNDICE ANALÍTICO...................................................................................159
Presentación En los últimos años la World Wide Web ha evolucionado mucho. Existe un verdadero abismo tecnológico y conceptual entre aquellas primeras páginas estáticas —con cuatro etiquetas para dar formato y unos pocos enlaces— y las actuales aplicaciones Web 2.0 como Google Docs, Facebook o Live Maps. Hay tanta diferencia entre ellas como entre los carruajes tirados por caballos y un Fórmula 1. El mundo de mediados de los 90 tampoco era el mismo y, desde los 70 millones de internautas estimados entonces a los casi 1.600 millones de 2009 (InternetWorldStats.com), la cosa ha cambiado mucho. Las diferencias estriban no sólo en lo que salta a la vista, sino también en lo que no se ve. Las expectativas de los usuarios no son los mismas, los lenguajes de programación tampoco. Antes era suficiente con mostrar texto plano y unos gráficos, hoy es preciso habilitar una interactividad total entre los elementos de la pantalla y el usuario. Cuando todos accedíamos a la WWW usando módems de 28.8 Kbps era aceptable esperar más de un minuto para recibir el contenido estático de una página. Y dábamos gracias a los dioses por ello ;-) Hoy en día no sólo debe haber una respuesta inmediata, sino que lo normal es que ni siquiera se evidencie en modo alguno que ha habido un viaje al servidor. Las fronteras entre las aplicaciones de escritorio y las aplicaciones Web son cada vez más difusas. ¡Bienvenidos al mundo de AJAX y las RIA (Rich Internet Applications)! ASP.NET es sin duda (y no es una opinión, sino un hecho) la plataforma de creación de aplicaciones Web más productiva que existe. La base fundamental sobre la que se sustenta esta tecnología y las diferentes características que ofrece, hacen posible esta visión moderna, interactiva y escalable de la Red. Este libro trata precisamente de esas tecnologías especializadas que marcan la diferencia entre una aplicación Web corriente y otra de la era Web 2.0 y más allá. ASP.NET 4.0 y Visual Studio 2010 nos traen las últimas mejoras de esta plataforma de desarrollo.
¿A quién va dirigido este libro? Por lo que acabo de comentar el lector ya se dará cuenta de una cuestión importante: este libro no es para principiantes. El contenido va dirigido a programadores de ASP.NET 2.0 que quieren dominar las principales tecnologías que aporta la última edición, ASP.NET 4.0. Se da por hecho que el lector tiene unos conocimientos, cuando menos fundamentales, de esta plataforma. Ahora bien, no se da por sentado nada en cuanto a las técnicas que se explican en el interior, de las que se parte de cero para facilitar el aprendizaje.
xiii
xiv Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Es especialmente interesante que el lector conozca un poco el leguaje JavaScript y una pizca de HTML, pues la tecnología AJAX se sustenta sobre ellos y vendrá bien para comprender el código de ejemplo.
¿Qué temas se tratan en el libro? Una gran parte del contenido se dedica a AJAX y todas las técnicas que hacen que funcione. Pero no te confundas, este no es el típico libro de AJAX que has visto por ahí. Va mucho más allá del uso del UpdatePanel que todo el mundo conoce, para adentrarse en la optimización para conseguir aplicaciones escalables. Un primer capítulo se dedica a enseñar los fundamentos de AJAX sin el apoyo de bibliotecas especializadas. Para que nos entendamos mejor: AJAX “a pelo”. Esto te ayudará a comprender bien su funcionamiento primordial y podrás responder mejor ante problemas que surjan más adelante en aplicaciones reales apoyadas en código de otros. ASP.NET AJAX es la biblioteca de Microsoft para crear páginas AJAX. El segundo capítulo presenta esta tecnología para sacarle partido sin tener que salirse de las técnicas habituales de todo programador .NET. Aprenderás a utilizar bien sus controles de servidor, que ofrecen una grandísima productividad con un rápido aprendizaje. Te proporcionará grandes ventajas y la usarás mucho, pero no está exenta de problemas. Todo programador preocupado por el rendimiento y la escalabilidad de sus aplicaciones debe ir más allá y no quedarse en este punto. Por eso, el tercer capítulo se centra en las capacidades del lado de cliente de ASP.NET AJAX. Se presenta la tendencia actual de las aplicaciones Web a trasladar cada vez más procesamiento al navegador, intercambiado datos directamente con el servidor. Aprenderemos lo necesario para poner en práctica esta visión, exponiendo y recibiendo datos a través de servicios que se consumen desde el navegador con JavaScript y el apoyo de ASP.NET AJAX. En la cuarta parte del libro llevaremos el concepto de AJAX “puro” al extremo gracias a las nuevas funcionalidades de plantillas para el lado cliente que nos ofrece ASP.NET 4.0. Esta nueva tecnología abre las puertas a un desarrollo Web super-eficiente que traslada toda la generación de la interfaz de usuario al navegador, dejando el servidor como un intermediario para mover datos. El quinto capítulo se centra en la nueva tecnología de generación de interfaces de gestión de datos: Dynamic Data. Gracias a ella podemos conseguir en minutos completas interfaces de administración de bases de datos para crear los típicos “Mantenimientos”. Pero como veremos, la tecnología va mucho más allá, proporcionándonos total flexibilidad para hacer lo que queramos en la gestión de datos sin apenas escribir código. Tendrás a tu alcance un nuevo nivel en la escala de la de productividad. La última parte del libro se centra en explicar un conjunto de controles Web destinados a crear interfaces para filtrado de datos. Se trata de los QueryExtender
Presentación xv
y las clases relacionadas con éstos. Con ellos, nuevos en ASP.NET 4.0, es muy sencillo conseguir avanzados sistemas de filtrado de información sin tener que escribir código. Combinándolos con los controles enlazados a datos podemos crear complejas páginas con listados de información en minutos.
Las herramientas que necesitas para trabajar Hace poco, después de una charla que impartí, tuve la oportunidad de hablar un buen rato con un par de emprendedores del mundo TIC que se me acercaron. Tras un tiempo de experiencia laboral por cuenta ajena decidieron volar solos, y un par de meses antes habían constituido una empresa para desarrollar aplicaciones Web. Me dijeron que se habían decidido a trabajar con PHP en lugar de con ASP.NET por que “en PHP es todo gratis y para programar con ASP.NET necesitamos pagar licencias a Microsoft.”. ¡Qué confundidos estaban! Y no sólo en esta afirmación, sino con otros muchos mitos y leyendas equivocados que existen sobre ASP.NET y sobre lo que algún día tengo que escribir largo y tendido. Menos mal que dieron conmigo para sacarlos de su error ;-) Para desarrollar con ASP.NET, tanto aplicaciones comerciales como para cualquier otro uso, no es necesario pagar ni un solo euro a Microsoft. Visual Studio dispone de unas ediciones especiales llamadas Visual Studio Express Edition que son gratuitas y de libre descarga. Apenas tienen limitaciones para desarrollar y en concreto la versión especial para desarrollo Web, Visual Web Developer Express, tiene toda la funcionalidad disponible en esta versión gratuita. Para la parte de desarrollo de bases de datos Microsoft ofrece también una versión Express de su gestor: SQL Server Express. Sus limitaciones son que sólo le está permitido ocupar 1 GB de RAM para caché de datos, utilizar un único procesador de la máquina (con los núcleos que tenga éste, da igual) y el tamaño máximo de las bases de datos que puede manejar la licencia es de 4 GB. Son unas limitaciones bastante amplias y es difícil llegar a superarlas en aplicaciones comunes en la PYME. Ofrece herramientas adicionales de administración y de reporting entre otras, y es perfecta para cualquier aplicación de gestión o para Internet. Y por supuesto sigue siendo gratis aunque nuestras aplicaciones sean comerciales. Las últimas versiones de Visual Web Developer Express y de SQL Server Express se pueden descargar libremente desde http://www.microsoft.com/express/. Te las recomiendo para practicar las explicaciones del libro. Si quieres funcionalidades de trabajo en equipo, desarrollar para Office o para móviles o poder depurar aplicaciones en remoto, puedes actualizarte a las ediciones comerciales de Visual Studio. Toda la información aquí: http://www.microsoft.com/ visualstudio/. Para aplicaciones empresariales de gran tamaño están disponibles las otras ediciones comerciales de SQL Server. Consulta sus características en http://www. microsoft.com/sqlserver/.
xvi Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
El código fuente de ejemplo Todos los ejemplos y demos desarrollados en el libro se pueden descargar desde la web de la editorial. Visita www.krasispress.com y busca el libro en el catálogo, bien navegando por las categorías o con la caja de búsqueda. En la ficha del libro existe un enlace para descargar los ejemplos de código. Descárgate el archivo en formato ZIP y descomprímelo en cualquier carpeta de tu ordenador. Para abrir los ejemplos desde Visual Studio lo mejor es usar la opción Archivo·Abrir·Sitio Web y elegir la carpeta del ejemplo en el que tengas interés. Por el nombre se deduce fácilmente a qué parte del libro corresponden. Para los ejemplos que usan datos he empleado la archiconocida base de datos Northwind. La puedes descargar en diversos formatos desde el sitio de descargas de Microsoft. Vete a http://download.microsoft.com y una vez allí introduce la palabra “Northwind” en el cuadro de búsqueda:
Figura 1.- Descarga de la base de datos Northwind
Se trata de una base de datos muy antigua, por eso pone que es una descarga para SQL Server 2000, pero no te preocupes pues te funcionará bien con cualquier versión moderna del gestor de datos. La he usado porque es la más popular entre los programadores de .NET, y hay una alta probabilidad de que la conozcas ya. Existe una versión nueva de esta base de datos, creada por la comunidad, que puedes descargar desde http://www.codeplex.com/NorthwindCommunity/. Es un proyecto reciente que trata de actualizar un poco el ejemplo original, pero no te aseguro que los cambios que hayan hecho vayan a funcionar con los ejemplos del libro, así que lo dejo a tu criterio, pero puedes probar.
Presentación xvii
Contacto con el autor y la editorial Puedes encontrarme y contactar conmigo a través de mi blog sobre desarrollo Web en www.jasoft.org. Ahí publico constantemente todo tipo de consejos, noticias y vídeos sobre el desarrollo para Internet con ASP.NET. Te recomiendo que lo visites. Me encanta recibir comentarios y críticas constructivas, pero no me gustan los que sólo se acuerdan de mi cuando necesitan algo “¿capisce?” ;-) La web de la editorial es www.krasispress.com. Desde allí puedes ponerte en contacto con el equipo editorial cuando quieras. Dentro de Krasis existe el proyecto campusMVP (www.campusmvp.com) del que probablemente hayas oído hablar. Se trata de formación on-line con cursos creados y tutelados por conocidos MVP de Microsoft, para que tú y tu equipo os forméis a vuestro ritmo y desde cualquier lugar preguntándole a los que más saben. En campusMVP tenemos un boletín mensual de noticias, trucos y “frikadas” varias que tiene varios miles de suscriptores encantados de recibirlo. Te recomiendo que te suscribas. También hemos puesto en marcha una página en Facebook (http://go.krasis.com/Facebook) con actualizaciones frecuentes sobre el mundo Microsoft y sus tecnologías, enlaces a artículos interesantes, noticias, vídeos prácticos, ofertas exclusivas para “fans”, etc... Si estás en esta red social no olvides hacerte fan de la página.
¡Comencemos! Gracias por tu interés en este libro. Espero que el esfuerzo de escribirlo haya valido la pena y que tras haberlo leído estés en condiciones de crear aplicaciones Web de alta calidad, escalables y sacando todo el partido a las últimas tecnologías Microsoft.
capítulo
1
ASP.NET y sus versiones
¿Por qué comenzar un libro que no está dirigido a principiantes con una cuestión, en apariencia, tan insignificante como ésta?... Pues porque no es un asunto trivial en absoluto. Como veremos, en ASP.NET los saltos en la numeración de las versiones no se corresponden en realidad con los cambios cuantitativos que cabría esperar de éstos, lo que causa gran confusión entre los programadores. Dada la cantidad de características existentes en esta potente plataforma de desarrollo Web, es muy importante saber qué hay disponible en cada versión, e incluso en cada actualización dentro de una versión. Por ello vamos a hacer una composición de lugar antes de ponernos a trabajar.
1.- La historia de ASP.NET hasta su versión 2.0 La primera versión de ASP.NET, la 1.0, apareció en el mercado en enero de 2002, junto con la edición inicial de Visual Studio para la plataforma .NET. Antes de este lanzamiento oficial, hubo diversas Betas en las que aún la tecnología llevaba el nombre de ASP+. Éstas estuvieron disponibles para experimentar desde mediados de 2000, por lo que en realidad la plataforma tiene mucho más recorrido del que se desprende de las fechas oficiales. La idea era sustituir al ASP 3.0 clásico -por eso le dejaron un nombre parecido, aunque no tengan nada que ver- y ofrecer un entorno de desarrollo para el servidor con grandes ventajas sobre lo existente en el mercado. Un punto importante era tratar de ofrecer los mismos paradigmas de desarrollo que las tradicionales aplicaciones de escritorio, pero aplicados a la Web. Esto implicaba sobre todo orientación a eventos y a objetos, uso de lenguajes compilados, independencia automática del navegador y productividad, mucha productividad. Con ASP.NET se empezaron a desdibujar los límites entre cliente y servidor en el desarrollo Web.
1
2 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Nota: Obviamente sin olvidarnos del inexcusable hecho de que hay una red de comunicaciones de por medio, con lo que ello implica en cuanto a transferencia de datos y velocidad, lo cual sigue siendo muy importante, claro.
El nuevo paradigma de desarrollo traído por los WebForms y la técnica de Postback, realmente innovador y tan característico de ASP.NET, hizo que desarrollar un formulario Web no fuese demasiado diferente de hacerlo para una aplicación de escritorio. Todas las características de productividad de esta primera versión convirtieron rápidamente a ASP.NET en una plataforma muy popular, pues permitía a los desarrolladores conseguir resultados impresionantes con muy poco tiempo y esfuerzo. En abril de 2002, poco después de un año, Microsoft actualizó mínimamente la plataforma sacando ASP.NET 1.1 y Visual Studio .NET 2003. En esta ocasión se incorporaron los controles móviles para el desarrollo Web orientado a PDA y teléfonos móviles, así como la validación automática de los elementos de la interfaz de usuario. Una actualización menor. El verdadero punto de inflexión en lo que se refiere al ámbito de desarrollo Web llegó en noviembre de 2005 (febrero de 2006 en España). En esta fecha se lanza ASP.NET 2.0 junto con Visual Studio 2005. Este sí que constituyó un cambio revolucionario dentro de ASP.NET; uno que influiría definitivamente en todas las versiones posteriores. Con todo el feedback recibido por Microsoft durante los cinco años previos, el equipo de desarrollo le dio un vuelco completo a la plataforma. Se introdujeron tantos cambios y características que se necesita un libro entero1 para explicarlas. Las más relevantes son las siguientes:
1
•
Nuevo modelo de compilación y separación de código e interfaz, que es un cambio de los propios fundamentos de trabajo de la plataforma.
•
Precompilación y despliegue de sitios Web para obtener un máximo rendimiento desde el primer momento. Soporte de sistemas de 64 bits.
•
El modelo de proveedores, con sus implicaciones en cuanto a extensibilidad de la plataforma.
•
Acceso a datos, incluso en varias capas SOA, sin necesidad de escribir código, gracias a un modelo declarativo.
•
Controles enlazados a datos de gran potencia, como las nuevas y mejoradas rejillas GridView, y controles de visualización y edición integrada como FormView o DetailsView.
El autor de este libro ha escrito también otra obra dedicada a explicar con detalle todas las tecnologías de ASP.NET 2.0, y que se puede adquirir en www.krasispress.com.
ASP.NET y sus versiones 3
•
Independencia de la interfaz de las páginas de su maquetado (Master Pages) y de su aspecto (Temas y Skins), fomentando la separación más absoluta entre interfaz y funcionalidad.
•
Interfaces de usuario para control de la seguridad mediante controles de arrastrar y soltar. Puedes construir toda la seguridad de tu aplicación sin escribir código alguno.
•
Creación de portales personalizables mediante WebParts.
•
Infraestructura de instrumentalización y monitorización de aplicaciones.
•
Nuevas técnicas de localización y globalización de aplicaciones.
•
Páginas asíncronas para sitios altamente escalables.
•
Controles de navegación.
•
Servicios de personalización de preferencias de usuario.
Este salto a la madurez de la plataforma abrió un nuevo mundo de posibilidades de desarrollo con alta productividad para sistemas Web empresariales, pero hizo que todos los programadores de la versión anterior tuvieran que actualizar sus conocimientos. La cuestión más importante, llegados a este punto, es señalar que el núcleo de ASP.NET es exactamente este mismo, el 2.0, en el resto de versiones que han salido desde entonces. Esto tiene una transcendencia fundamental, porque podemos afirmar que si conoces bien ASP.NET 2.0 conoces todo lo necesario para sacarle partido a las siguientes versiones, porque en esencia son la misma, como enseguida veremos. Sin embargo, si provienes de la versión 1.x tendrás que aprenderlo todo casi desde cero.
2.- La versión 3.0 de .NET La versión 3.0 de la plataforma .NET se presentó en sociedad en noviembre de 2006, justo un año después de la anterior. En realidad, esta versión 3.0 no era otra cosa que la plataforma .NET 2.0 junto con cuatro nuevas API especializadas que estaban construidas sobre ésta: • Windows Communication Foundation (WCF): para la creación de sistemas distribuidos basados en conceptos SOA (arquitecturas orientadas a servicios). A pesar de lo que pueda parecer por el poco afortunado nombre —que confunde a algunos desarrolladores—, estos servicios no están atados en absoluto a la plataforma Windows, sino que siguen los estándares de la W3C y pueden interoperar con cualquier otro sistema operativo y lenguaje de programación. •
Windows Workflow Foundation (WF): que permite el desarrollo de sistemas adaptables basados en flujos de trabajo tanto secuenciales como de estado, persistentes en el tiempo y con otros servicios añadidos.
4 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
•
Windows Presentation Foundation (WPF): la nueva API para creación de interfaces de usuario vectoriales avanzadas. Va mucho más allá de los sistemas tradicionales de ventanas para ofrecer un paradigma mixto entre el escritorio y la Web. Un subconjunto de WPF, multiplataforma y específico para la Web, es lo que se conoce como Silverlight.
•
Windows CardSpace: un sistema cliente para la gestión de identidades en Internet y en la empresa. Es, con diferencia, la API de la versión 3.0 que menos adopción ha tenido.
Si bien todas estas API adicionales son muy importantes y abren muchas vías de mejora en la programación de sistemas empresariales, lo cierto es que no dejan de ser añadidos a la plataforma, que dependen de ésta, pero el núcleo sigue siendo la versión 2.0 sin cambios de ningún tipo. En lo que respecta al desarrollo Web y ASP.NET, la versión 3.0 no aporta característica nueva alguna. Obviamente, podemos sacarle partido a alguna de estas API para añadir características a nuestras aplicaciones Web (por ejemplo, un servicio Web creado con WPF o un flujo de decisión basado en WF), pero en lo que se refiere a la plataforma ASP.NET, en ese momento sigue siendo la misma que había un año atrás.
3.- La versión 3.5 de la plataforma Mientras tanto, el mundo de las aplicaciones Web empieza a ver la invasión de AJAX. Estos desarrollos, a los que dedicamos una gran parte de este libro, se basan en la interacción estrecha con el usuario desde la interfaz del navegador, aparentando en la medida de lo posible que no hay comunicación visible con el servidor. La idea es que las aplicaciones Web se parezcan más aún a las de escritorio, sin recargas de páginas y dando la sensación de que todo ocurre en el equipo del usuario. Microsoft no puede permanecer ajena a esta tendencia que ellos mismos habían creado (como se explica en el primer capítulo de AJAX), así que en enero de 2007 aparece una API de manera independiente al desarrollo de la línea principal de .NET: ASP.NET AJAX, por aquel entonces conocida con el nombre en código de “Atlas”. Esta API para crear interfaces web de alta velocidad de respuesta y sin recarga de páginas, es un éxito instantáneo y se puede instalar paralelamente a ASP.NET trabajando sobre la versión 2.0 existente y con Visual Studio 2005. Unos meses después, en noviembre de 2007, aparece Visual Studio 2008, que incluye la versión 3.5 de la plataforma .NET. Esta versión sí que contiene cambios sustanciales en los lenguajes C# y VB y en los correspondientes compiladores. La mayor parte de dichos cambios tienen que ver con añadidos para dar soporte al nuevo lenguaje integrado de consultas LINQ (Language INtegrated Queries). Es decir, el núcleo de la plataforma sigue siendo el mismo (versión 2.0), pero con modificaciones en los lenguajes y algunas API añadidas, en especial las relacionadas con LINQ.
ASP.NET y sus versiones 5
Para acabar de liar las cosas, aunque la versión de la plataforma es la 3.5, la de los lenguajes no coincide. Por ejemplo, la versión de C# aparecida entonces es la 3.0, lo que añade más confusión para los programadores. En este momento se incorpora oficialmente el soporte para AJAX a la parte de desarrollo Web, por lo que a partir de entonces podías contar con que la plataforma tendría incorporado AJAX sin necesidad de instalarlo de manera separada. Además, para dar soporte a LINQ desde aplicaciones Web se añadió una biblioteca llamada System.Web.Extensions.dll que incluía tres nuevos controles: LinqDataSource, ListView y DataPager. Por lo tanto, lo que se dio en llamar ASP.NET 3.5 era en realidad lo siguiente: ASP.NET 2.0 + ASP.NET AJAX + 3 controles De nuevo importantes mejoras globales, gracias a LINQ para el acceso a datos, pero el mismo núcleo en lo que al desarrollo Web respecta. La interfaz de trabajo de Visual Studio 2008 ofrece también algunas características nuevas de productividad para la Web, sobre todo el soporte de Intellisense y depuración para el lenguaje JavaScript.
4.- Un Service Pack que es mucho más que un parche ¿Qué tendrá de especial el mes de noviembre para Microsoft? No lo sé, pero si te has fijado, es el mes en el que desde hace muchos años salen todas las grandes versiones de sus plataformas de desarrollo :-). Así, en noviembre de 2008 (como no podía ser de otra manera) aparece el Service Pack 1 para .NET 3.5, es decir, la nueva versión de la plataforma: .NET 3.5 SP1. Tradicionalmente, los Service Pack están pensados para agrupar en una sola descarga una recopilación de ajustes, resoluciones de fallos y parches de seguridad con el objeto de facilitar su aplicación a los usuarios. En realidad no deberían incluir nuevas funcionalidades. Incluso la propia Microsoft los define de esta manera: http:// support.microsoft.com/sp. Pero lo cierto es que en diversas ocasiones el gigante de Redmond los ha utilizado para agregar grandes bloques de funcionalidad a algunos productos. Este fue el caso del SP1 para .NET 3.5. En esta actualización, Microsoft incluyó novedades en todos los ámbitos, siendo la más visible de todas ellas la aparición de Entity Framework, su nuevo ORM para acceso a datos. En el ámbito del desarrollo Web aparecieron también varias características, aunque todas se sustentaban sobre ASP.NET 2.0. Entre ellas se encontraban: •
Dynamic Data: un nuevo sistema de creación automática de interfaces de usuario Web a partir de modelos de datos.
•
Control EntityDataSource: para dar soporte a acceso a datos declarativo a través de Entity Framework.
6 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
•
ADO.NET Data Services: creación y exposición automática de servicios Web para acceso a datos en modo REST, fundamentalmente basándose en Entity Framework.
•
Enrutamiento: soporte para definición dinámica de rutas virtuales en el servidor y su mapeado a archivos existentes o no. Especialmente pensado para dar soporte a ASP.NET MVC, del que hablaré en breve. Muy sencillo de utilizar. Toda la información aquí: http://msdn.microsoft.com/es-es/library/ cc668201.aspx.
•
Mejoras en AJAX: en concreto, soporte para el historial del navegador, un sistema de combinación de scripts para mejorar los tiempos de descarga de estos archivos, y un control para mostrar vídeos.
No está mal para un simple parche.
5.- .NET 4.0 y Visual Studio 2010 Y por fin llegamos al final de la historia (por el momento). A finales de 2009 hacen su aparición Visual Studio 2010 y la versión 4.0 de la plataforma. En lo que se refiere a Visual Studio, los cambios son sustanciales. Para empezar, el entorno es completamente nuevo y está basado en WPF, con interesantes mejoras para la escritura de código, la depuración y las superficies de diseño. En lo que se refiere al núcleo de la plataforma y las API relacionadas, hay muchos perfeccionamientos y añadidos, si bien los más importantes, en mi opinión, tienen que ver con la introducción de soporte en la plataforma para el desarrollo paralelo. En la actualidad se han popularizado los sistemas con más de un procesador y, sobre todo, los que llevan procesadores de múltiples núcleos (conocidos como manycore), que son hoy el estándar de la industria y que se incluyen hasta en los portátiles de gama más baja. Estos sistemas abren grandes posibilidades para el desarrollo de aplicaciones más eficientes y de mayor rendimiento, sacando partido a la capacidad de ejecutar en paralelo múltiples hilos de trabajo. El desarrollo de aplicaciones con múltiples hilos, sin embargo, reviste una gran complejidad para el programador, y con frecuencia se da la paradoja de que aplicaciones con multisubproceso se ejecutan peor en sistemas manycore que en sistemas sencillos más antiguos. Gracias a las extensiones para el desarrollo paralelo incluidas en .NET 4.0, la creación de este tipo de aplicaciones asíncronas y multi-subproceso se simplifica en gran medida. El nuevo modelo permite a los desarrolladores la escritura de código paralelo que es eficiente, granular y escalable, pero sin tener que lidiar directamente con las dificultades de la creación y sincronización de hilos, y además usando un lenguaje más natural para el programador.
ASP.NET y sus versiones 7
Otra importante novedad es el soporte para lenguajes dinámicos, del estilo de Lisp, PHP, Ruby o incluso JavaScript, que ahora son mucho más fáciles de incorporar a la plataforma. En lo que respecta a la parte de desarrollo Web, la principal novedad es, sin duda, el nuevo sistema de plantillas enlazadas a datos en el navegador, parte de la nueva versión de ASP.NET AJAX. Esta característica abre la posibilidad de crear aplicaciones AJAX más “puras” con gran trasvase de funciones desde el servidor al cliente. Las plantillas de lado cliente se tratan con profundidad en esta obra. También se ha añadido un nuevo control Chart para generar gráficos, y ciertas mejoras en Dynamic Data. Estas últimas también las estudiaremos. Adicionalmente, ASP.NET 4.0 incluye una nueva tecnología de desarrollo Web llamada ASP.NET MVC, que se basa en el patrón de diseño Modelo Vista-Controlador (de ahí el nombre). Éste constituye un sistema de desarrollo Web paralelo al ASP.NET tradicional, usando otro tipo de paradigmas completamente diferentes. Esta tecnología ha surgido como un proyecto independiente y ahora se incorpora con pleno soporte a la plataforma en la versión 4.0. Hay quien la adora y le ve grandes ventajas, y hay quien opina que es un paso atrás en el modo de desarrollar aplicaciones Web. En cualquier caso, se trata de una tecnología para el desarrollo Web completamente aparte de la tradicional y que en cierta medida “va por su cuenta”. Por este motivo en este libro, centrado en el desarrollo ASP.NET tradicional, no se incluye ASP.NET MVC. Existen en .NET 4.0 otros muchos otros añadidos y mejoras de diverso calado, muchos de los cuales provienen del SP1 para .NET 3.5 y que se han incorporado ya como base a la plataforma. En http://tinyurl.com/PDC2008-NETFX4PDF hay para descarga un poster de alta resolución que permite ver todas las novedades de .NET 4.0 en cada una de sus áreas de una forma resumida y sencilla.
6.- De qué trata este libro (y qué deja fuera) Como hemos podido comprobar en esta breve historia de .NET, existe una cierta complejidad para entender sus versiones y lo que nos proporciona cada una de ellas. Lo que sí podemos afirmar es que, en lo que respecta al desarrollo Web, todas las recientes se sustentan en la funcionalidad proporcionada por la 2.0. Las versiones 3.0, 3.5, 3.5 SP1 y 4.0 incluyen añadidos y mejoras que trabajan con 2.0 por debajo y aportan funcionalidades específicas, como el trabajo asíncrono desde el cliente o la creación de interfaces automatizadas. Por ello, este libro se va a centrar en las principales tecnologías aparecidas desde la versión 2.0 de ASP.NET y hasta la versión actual, que las contiene a todas. Así, centraremos gran parte de la obra en el desarrollo de aplicaciones AJAX, empezando por los fundamentos de esta tecnología, la creación de aplicaciones AJAX basadas en el servidor (que es la más común pero la menos óptima) y por fin, el cambio de filosofía que supone transferir gran parte de la lógica desde el servidor al cliente, que es la parte más compleja (y también la más interesante).
8 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Con ello abarcaremos todo lo que es necesario saber de ASP.NET AJAX, desde la versión descargable independientemente para .NET 2.0 hasta las novedades aparecidas con .NET 3.5 SP1 y las últimas técnicas avanzadas de ASP.NET 4.0, por lo que se tendrá una referencia muy completa. Otra tecnología que no podíamos dejar fuera es Dynamic Data. Aparecida en .NET 3.5 SP1, es casi una desconocida para la gran mayoría de los programadores Web. Esto es una verdadera lástima, pues esta tecnología ofrece grandes posibilidades y mejoras de productividad. Con ASP.NET 4.0 se han incluido algunos perfeccionamientos adicionales y se espera que empiece a utilizarse de manera generalizada. En este libro aprenderás lo necesario para sacarle todo el partido. Para terminar he incluido también un completo capítulo sobre los nuevos controles para generación de interfaces de filtrado. Existen otras pequeñas mejoras de menor calado que han aparecido con ASP.NET 4.0 para la parte de desarrollo Web. No las he incluido aquí pues son una amalgama de diferentes cosas no relacionadas y hay información exhaustiva en Internet sobre ellas. En mi blog (www.jasoft.org) podrás encontrar información sobre la mayoría en diversas entradas que he ido añadiendo y añadiré en el futuro. Hay muchas otras tecnologías que se pueden utilizar en los desarrollos para Internet con .NET, pero que no son específicas para este propósito: LINQ, Entity Framework, Windows Communication Foundation, Silverlight/WPF, Workflow Foundation... Cualquiera de ellas es lo suficientemente extensa y compleja como para merecer un libro dedicado, por lo que no tiene sentido incluirlas en esta obra. Si tienes interés en aprender estas tecnologías te recomiendo que visites www.campusmvp.com, en donde encontrarás los mejores libros y cursos del mercado sobre todas ellas.
7.- En resumen Una vez que sabemos ubicarnos entre la maraña de versiones y tecnologías para el desarrollo Web, ya estamos en condiciones de comenzar a aprender las principales características de las últimas versiones de ASP.NET. ¡Comencemos!
capítulo
2
Fundamentos de AJAX Desde sus comienzos y hasta hace relativamente poco tiempo las interfaces de usuario de las aplicaciones Web han sido más o menos siempre iguales. Las limitaciones propias del protocolo HTTP (Hyper Text Transfer Protocol) utilizado en las páginas Web han impuesto el tradicional modelo de “petición-respuesta-procesado en el navegador” (a partir de ahora PRP) y vuelta a empezar. Los pasos que sigue una aplicación Web corriente para funcionar suelen ser los siguientes: 1. El usuario solicita una página 2. El servidor devuelve el contenido HTML correspondiente a ésta, normalmente generado a partir de alguna tecnología de servidor (como ASP.NET o PHP) 3. El navegador recibe este HTML, lo procesa y visualiza el contenido resultante. 4. El usuario interactúa con el HTML, envía un formulario al servidor o pulsa un enlace, y se repite el ciclo desde el paso 1: se solicita la página, se devuelve su contenido, se procesa y se visualiza. Este proceso es el más natural para HTTP —pensado desde luego para funcionar así— pero tiene el problema de que para obtener una página prácticamente igual a la inicial pero con pequeñas modificaciones es necesario recargar la página completa. Esta situación es especialmente común desde que ASP.NET apareció en escena hace ya unos cuantos años, con su novedoso sistema de “postback”. Gracias a este concepto una aplicación Web se programa prácticamente igual que una de escritorio, respondiendo a eventos y accediendo directamente a las propiedades de los objetos. El problema del sistema es que cada uno de los postback al servidor hace que se recargue la página completa, lo cual es percibido de manera evidente por los usuarios (es decir “se nota” el refresco de la página) y crea una sensación poco amigable. Además si el retorno de la página tarda más que unos pocos milisegundos se pierde capacidad de respuesta de la página puesto que, durante el proceso de petición-respuesta-procesado, la interfaz del navegador no responde. 9
10 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Si bien el concepto de “postback” es extremadamente útil, las actuales tendencias en el desarrollo Web hacen que éstas sean cada vez más parecidas a aplicaciones de escritorio. Esto implica que los molestos y a la vez inevitables viajes al servidor no deberían ser percibidos por los usuarios. La sensación para éstos debe ser la de que la aplicación está todo el tiempo en su equipo, dejando de lado al servidor, como en una aplicación de escritorio tradicional.
1.- Interfaces de usuario avanzadas Si has usado alguna vez Facebook, Hotmail, GMail o alguna aplicación web de última hornada sabes perfectamente de qué estoy hablando. Toma por ejemplo el caso de Facebook. Cuando actualizas tu estado o introduces un comentario para un amigo, por detrás se está ejecutando el proceso PRP antes descrito, sólo que tú no lo notas ya que se realiza de manera asíncrona (para no bloquear la interfaz) y actualiza únicamente un trocito de la página, que es el que se ve afectado por tu acción. Imagínate que cada vez que dices “Me gusta” en Facebook se recargara por completo la página entera. Se haría insufrible y dudo mucho que tuviera el éxito actual. Lo mismo se aplica a los clientes Web para correo electrónico como los mencionados. Ya nadie concibe pulsar sobre una de tus carpetas de correo (o tags en el caso de GMail) en el lateral, y que de repente se cargue de nuevo la página completa. La experiencia debe ser integrada, como la de cualquier aplicación de escritorio.
Figura 1.- La interfaz de Facebook usa de manera intensiva técnicas AJAX.
Desde que el HTML Dinámico (HTML + JavaScript) y las hojas de estilo CSS hicieron su aparición, cada vez más aplicaciones hacen uso de las posibilidades de modificación “al vuelo” de contenidos que estas tecnologías brindan, consiguiendo los resultados que acabo de describir. En la actualidad todos los navegadores ofrecen soporte para DHTML por lo que si quieres escribir programas complejos de lado
Fundamentos de AJAX 11
de cliente, que interactúen con los contenidos de la página, no implica como antes tener que dejar fuera a parte de tus posibles usuarios. A la combinación de HTML dinámico con tecnologías de servidor se le denomina de manera genérica AJAX. Este simpático acrónimo hasta hace poco asociado con el apasionante mundo de la limpieza y con el fútbol, viene del concepto en inglés Asynchronous JavaScript And XML. Se basa en el uso de un objeto llamado XMLHttpRequest, presente en todos los navegadores modernos, que como es de imaginar por su nombre sirve para hacer peticiones al servidor de documentos XML a través del protocolo HTTP. Utilizado este objeto se solicitan al servidor datos en formato XML que, una vez recibidos en el navegador, es posible manipular mediante código JavaScript y mostrar el resultado dentro de los elementos de la página. Esta es la idea original de esta técnica, pero como comprobaremos en breve, de este punto inicial a lo que existe actualmente las cosas han cambiado mucho. Aunque casi todo el mundo se piensa que esto de AJAX es un invento de Google y su potente cliente Web de correo electrónico GMail (primera aplicación que en verdad lo popularizó), el objeto XMLHttpRequest apareció originariamente junto a las bibliotecas XML de Microsoft (MSXML) a finales de los años noventa del siglo pasado. El concepto original de esta tecnología fue creado también por Microsoft (se llamaba Remote Scripting), el primer navegador en soportarlo fue Internet Explorer y la primera aplicación de este tipo fue Outlook Web Access (OWA), para acceder a buzones Exchange. Como veremos enseguida, aparte de que su nombre original haya perdurado por ser simpático, la realidad es que AJAX no siempre es asíncrono ni tampoco siempre usa XML. De hecho lo más habitual es que hoy en día utilice otro formato para transferir los datos: JSON, que estudiaremos luego. Lo que de verdad constituye el corazón de de esta técnica es el objeto XMLHttpRequest. En este capítulo vamos a estudiar los fundamentos de la tecnología para que no dependas de biblioteca alguna a la hora de implementar estas características, y sobre todo -seamos realistas, casi nunca usarás esto “a pelo”- para que cuando uses una de dichas bibliotecas comprendas lo que hay debajo y puedas determinar posibles problemas. Conociendo bien los conceptos subyacentes tendrás muchas más herramientas para sacarle partido a las bibliotecas construidas sobre ellos, como por ejemplo la parte cliente de ASP.NET AJAX u otras muy conocidas como jQuery, YUI, script.aculo.us, MooTools, Dojo, etc...
2.- Un poco de teoría: el objeto XMLHttpRequest Para sacar partido a AJAX, aparte de tener conocimientos de HTML y JavaScript, el primer objeto que debemos conocer a fondo es el mencionado XMLHttpRequest. Se trata de una clase disponible en todos los navegadores modernos que permite lanzar desde JavaScript peticiones de recursos GET Y POST a través de HTTP. Dicho en lenguaje simple, lo que esta clase nos permite es simular mediante código JavaScript llamadas al servidor como si éstas hubieran sido hechas por los usuarios.
12 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
El efecto es el mismo que si el usuario hubiese enviado un formulario o pulsado sobre un enlace, sólo que nuestro código es el que tiene el control sobre ello y lo gestiona de manera independiente al contenido actual de la página (en la jerga se suele decir que lo gestiona “por debajo”). Aunque en el caso de Internet Explorer se sigue exponiendo su funcionalidad como un objeto ActiveX en el resto de los navegadores (Firefox, Safari, Chrome, Opera...) éste forma parte ya de sus clases nativas. Los métodos y propiedades básicos de esta clase que debemos conocer son los siguientes (los corchetes indican parámetros opcionales): •
open(método, URL, [asincrono], [usuario], [clave]): sirve para abrir una conexión al servidor. No envía ni obtiene información, sólo se conecta. El primer parámetro indica de qué manera queremos enviar la petición al servidor: por GET o por POST. El segundo es la dirección Web a la que vamos a llamar. El tercer parámetro es booleano y sirve para indicar si la conexión se realizará de forma asíncrona (por defecto) o no. Los dos últimos parámetros sirven para especificar un nombre de usuario y una contraseña de acceso para recursos protegidos por autenticación básica, si bien esto es bastante absurdo pues estarán escritos en claro en el JavaScript, por lo que raramente se utilizan.
•
send(contenido): envía una petición. Si es un envío por POST se pueden incluir los datos a enviar en su único parámetro, si no se usa un nulo.
•
abort(): cancela un envío/petición abierto previamente.
•
onreadystatechange: a esta propiedad se le asigna una referencia a un método JavaScript que será llamado automáticamente cuando se descargue del todo la URL remota (cuando se llama a ésta asíncronamente).
•
readyState: informa del estado de la petición: o 0=no iniciada o 1=cargando o 2=terminada pero sin procesar o 4=completada.
•
status: código de estado HTTP resultado de la petición: por ejemplo 200 (éxito), 404 (no encontrado), etc... Se corresponden con los códigos de estado de HTTP (los puedes consultar aquí: http://www.w3.org/Protocols/HTTP/ HTRESP.html).
•
statusText: mensaje de información correspondiente al estado anterior.
•
responseXML: documento DOM que representa el XML devuelto por la petición. Sólo se utiliza si lo que esperamos obtener es un documento XML.
Fundamentos de AJAX 13
En la actualidad no se utiliza casi nunca pues el XML se ha abandonado en AJAX a favor de JSON, que luego veremos. •
responseText: el contenido puramente textual del recurso remoto. Es la que usaremos habitualmente para obtener el contenido del recurso solicitado.
Aunque dispone de algunos métodos y propiedades más, con estas tendremos suficiente para el 99% de los casos que nos vamos a encontrar.
3.- Basta de teoría: vamos a la práctica Veamos cómo se usa la clase XMLHttpRequest en la práctica con un ejemplo sencillo. Consideremos algo simple como el ejemplo de la figura:
Figura 2.- Ejemplo de lista desplegable auto-completada con AJAX
Hay una lista desplegable que contiene una serie de categorías. Al elegir una de éstas, en la lista de al lado deberán aparecer los elementos que contiene rellanándolos dinámicamente con lo que nos indique una página en el servidor, pero sin recargar la página. Este ejemplo es muy sencillo pero nos ayuda a centrarnos sólo en la parte que nos interesa, que es la de cliente y en cómo realizar las llamadas AJAX. Lo que debemos hacer para que esto funcione es responder a los eventos de cambio de selección de la primera lista y lanzar “por debajo” una petición a una página del servidor que nos devolverá los elementos de la lista secundaria. En un ejemplo real los elementos se obtendrían, en la página del servidor, seguramente desde una base de datos.
14 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Lo primero que debemos hacer para lanzar una llamada al servidor desde nuestro código JavaScript es obtener una referencia a un objeto de la clase XMLHttpRequest que será el que utilizaremos. Dado que Internet Explorer es diferente a los demás navegadores en este aspecto, debemos distinguir con quién estamos trabajando para poder instanciarlo. Podemos encapsular esta funcionalidad con el código del listado 1:
Listado 1 function getHttpRequest() { var httpReq; //Si es Mozilla, Opera, etc... if (window.XMLHttpRequest) { httpReq = new XMLHttpRequest(); } else //Internet Explorer lo expone como control Active X { httpReq = new ActiveXObject(“Microsoft.XMLHTTP”); } }
Así dispondremos de una función getHttpRequest que nos devolverá una instancia del objeto XMLHttpRequest independientemente del navegador. Una vez que tenemos una referencia al objeto usaremos sus métodos y propiedades para lanzar una petición a la página de servidor que nos interese, por ejemplo así: http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true) http.send(null);
En la segunda línea, y antes de llamar a la página del servidor, establecemos una referencia a la función que se llamará automáticamente cuando cambie el estado de la petición que vamos a lanzar. Ello no implica que ésta tenga éxito o que sea llamada sólo cuando termine, como veremos enseguida. La tercera línea abre el conducto de petición al servidor para cargar una determinada URL de modo asíncrono (true en el tercer parámetro). En este caso usará el método GET pues no enviamos datos al servidor. Por fin debemos hacer la llamada (el open de la línea anterior no la hace sólo la prepara), usando para ello el método send. Se le pasa un nulo porque no enviamos ninguna información extra (es una petición GET). El hecho de que sea una llamada asíncrona hará que se devuelva el control inmediatamente a JavaScript al pasar por esta línea, por lo que podríamos tener más código a continuación para llevar a cabo más tareas sin que se viera interrumpida la ejecución ni la interacción con el usuario. Podríamos usar llamadas síncronas ( false en el tercer parámetro) para lanzar varias
Fundamentos de AJAX 15
llamadas seguidas con la certeza de que se ejecutarán en un determinado orden. Es decir, como vemos, AJAX en realidad no siempre debe ser asíncrono. Obviaremos de momento el código de la página del servidor que podría ser cualquiera (acceder a una base de datos para obtener los elementos, leer un archivo o la memoria, etc...). Lo único verdaderamente importante es ponernos de acuerdo en cómo la página del servidor va a devolver los resultados de la petición. Podemos complicarlo todo lo que queramos usando XML o cualquier otra notación que consideremos oportuna. Más tarde retomaremos este tema. De momento para acabar con el ejemplo vamos a suponer simplemente que el servidor nos devuelve una lista, separando con comas los elementos que se desean mostrar en el control secundario. Como hemos visto se define una función llamada ‘finCarga’ que es llamada de manera automática al ir cambiando el estado de la petición. Veamos cómo es su aspecto en el listado 2:
Listado 2 function finCarga() { if (http.readyState == 4) //4: completado { if (http.status == 200) //200: OK { res = http.responseXML; Procesarespuesta(); } else //Se produjo un error { alert(“No se pudo recuperar la información: “ + http.statusText); } } }
Lo único que hacemos aquí es detectar cuándo se ha terminado la petición (readyState será igual a 4 como hemos visto antes) y que ésta haya sido una petición exitosa (el código de estado HTTP debe ser 200). Si el código HTTP es 404 (no encontrado), 500 (error en el servidor) u otro cualquiera se advierte con un mensaje al usuario. En caso positivo lo único que hacemos es anotar la respuesta del servidor en una variable global de la página (res en el ejemplo) y procesar el resultado adecuadamente. En este caso se separan los elementos con las comas y se carga la lista secundaria. Dado que es un código JavaScript trivial y no aporta nada al tema que nos ocupa no lo he incluido aquí, pero lo puedes ver en el archivo con los ejemplos del libro. Lo único que se usa normalmente al mostrar los resultados son los conocidos métodos getElementsByTagName y getElementByID de HTML dinámico y del DOM. Estudia el ejemplo incluido en el ZIP para ver exactamente como se ha hecho.
16 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Este ejemplo tan sencillo constituye en realidad todo lo que es necesario saber sobre los fundamentos de funcionamiento de AJAX.
4.- Problemas típicos de AJAX y sus soluciones Ahora que ya conocemos los rudimentos de AJAX vamos a ver cuáles son los principales problemas que nos podemos encontrar al usar estas técnicas, y que en ocasiones pueden ser complicados de detectar. Probablemente, serán los mismos que nos encontraremos si utilizamos alguno de los paquetes específicos para AJAX como ASP.NET AJAX y las otras mencionadas, por lo que debemos ser conscientes de ellos. Los más importantes son los siguientes: 1. Llamadas fuera del dominio. 2. Llamadas que producen errores o que no vuelven jamás. 3. Envío de datos al servidor. 4. Contenidos no actualizados debido a cachés.
4.1.- Llamadas fuera de dominio Una vez que uno empieza a juguetear con las posibilidades de AJAX enseguida se nos ocurren ideas geniales para sacarle partido. La más obvia, claro está, es la de utilizar las técnicas para acceder desde el cliente a ciertos Servicios Web ajenos de utilidad ubicados en Internet. Así, dado que los Servicios Web están basados en XML, es muy fácil procesar lo que devuelven con las técnicas descritas para, por ejemplo, realizar búsquedas en Amazon con su API, seguir una subasta en eBay, enviar “posts” a nuestro blog, consumir fuentes RSS, etc... Todo esto es estupendo pero tiene un gravísimo problema: los navegadores, por cuestiones de seguridad, bloquean todas las peticiones realizadas mediante XmlHttpRequest a dominios que no sean el que aloja la página desde la que se está usando. En realidad se trata de una restricción bastante lógica y que aparece en otras partes del navegador, como las cookies, el acceso a variables de JavaScript entre marcos, los objetos Flash o los applets de Java. Pero esto, claro está, supone una limitación importante para ciertos tipos de aplicaciones AJAX que podríamos desarrollar, como las de los ejemplos comentados. La pregunta ahora es entonces: ¿Cómo solventamos esta situación? En Internet Explorer basta con bajar el nivel de seguridad para que ya funcione correctamente, pero no es una buena solución (no le puedes pedir esto a tus usuarios). En otros navegadores (Firefox, Opera, Chrome y Safari) no hay forma de saltarse esta restricción. Existe una salvedad en Firefox que consiste en firmar digitalmente el JavaScript que usas, pero tampoco vale de mucho pues sólo funcionaría en este navegador. La única forma de solucionarlo de manera independiente al navegador es, aunque sea de Perogrullo, hacer que no dependa de éste, es decir, llevarnos el problema al
Fundamentos de AJAX 17
servidor. Para ello lo que debemos hacer es construir un servicio proxy que esté en nuestro servidor (al que sí podremos llamar con AJAX) y que sea éste el que se encargue de realizar la llamada a otros dominios devolviendo el resultado a nuestro JavaScript (directamente o pre-procesándolo de algún modo). En .NET esto implica generalmente crear un manejador de peticiones con extensión .ashx o un servicio Web propio que se encargue de realizar por nosotros las peticiones que nos interesen. ¡Mucho ojo con esto!. Normalmente este tipo de servicios -al igual que los que se encargan de leer archivos de disco de manera genérica y otros similares- son un verdadero peligro de seguridad si no los programamos bien. Si optas por esta solución lo mejor es que tomes varias precauciones de cara a la seguridad: tener muy acotados los servicios o URLs a los que se puede llamar desde el proxy. Lo mejor es identificarlos a cada uno con un número o código decidiendo a cuál se llama (con una cláusula switch en C# o Select Case en VB.NET), nunca poniendo la URL directamente en la llamada desde JavaScript. Otra medida adicional es tratar de identificar al Script llamante de alguna manera: mediante una cabecera que te debe enviar, comprobando el dominio del “referer” y cosas similares. Está claro que un cracker experimentado se puede saltar esto pero le dará bastante trabajo y elimina de un plumazo a los aficionados que quieran hacer uso ilícito de tu servicio. Si puedes limita el número máximo de llamadas seguidas que se puede hacer desde una determinada IP o, mejor, en una determinada sesión de servidor. Toda precaución es poca.
4.2.- Gestión de errores y llamadas que no vuelven No podemos asumir que las llamadas que hagamos al servidor van a funcionar siempre. Puede haber un error en el código del servidor, es posible que haya cambiado la URL y que no aparezca la página que llamamos, o que haya errores de permisos, etc... Lo que pase en el servidor está fuera de nuestro control. Ante eso hay que estar preparado. La forma de controlar estas situaciones es, como en cualquier componente de comunicaciones por HTTP, a través del código de estado que devuelva el servidor. Todo esto ya se había apuntado antes y se había tenido en cuenta en el código del listado 2. Podríamos afinar más en el mensaje de error y devolver uno diferente según el código de estado. Hay, sin embargo, una situación menos frecuente pero más peligrosa que se puede producir: que la llamada asíncrona al servidor no vuelva o no lo haga en un tiempo razonable, es decir que se produzca lo que se denomina un timeout. ¿Qué hacemos en ese caso? No podemos contar con la notificación de final de carga puesto que, al no regresar la llamada no saltará, así que el listado 2 no nos sirve. Lo que se hace en estos casos es establecer un temporizador con el tiempo máximo que deseemos esperar, para que al cabo de ese intervalo la petición sea anulada directamente, sin esperar más que llegue la respuesta. Podemos verlo en el listado 3:
18 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Listado 3 http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true) var tmrAnular = setTimeout(“AnularPeticion()”, 20000); //20 segundos http.send(null); function AnularPeticion() { http.abort(); } function finCarga() { if (http.readyState == 4) //4: completado { clearTimeOut(tmrAnular); if (http.status == 200) //200: OK { res = http.responseXML; Procesarespuesta(); } else //Se produjo un error { alert(“No se pudo recuperar la información: “ + http. statusText);
}
}
}
Se ha modificado el código de llamada anterior para añadir la creación de un temporizador que se encarga de anular la petición al pasar un tiempo determinado (en este caso de 20 segundos pero puede ajustarse a cualquier otro valor). Nótese también como en el evento de fin de carga eliminamos el temporizador (que ya no nos hace falta) cuando la petición termina de procesarse, en caso de que regrese.
4.3.- Envío de datos al servidor Normalmente cuando pensamos en AJAX, es decir, en llamadas asíncronas a servicios, lo hacemos desde el punto de vista de obtener información: llamo a una página que me devuelve unos valores y los muestro en la interfaz de usuario. Aunque este
Fundamentos de AJAX 19
es el uso más común de AJAX lo cierto es que también es muy útil usarlo en el sentido inverso, para enviar datos al servidor. Las utilidades y necesidades que cubre son múltiples y de hecho hay muchos sistemas que le sacan partido. La forma más sencilla y directa de enviar datos simples al servidor es incluirlos en la URL a la que llamamos como parámetros GET: urldestino.aspx?Parametro1=1234&Parametro2=5 Aunque esto puede servirnos para cosas muy sencillas no es lo que necesitaremos en la mayor parte de los casos. Lo habitual es que la información haya que enviarla con el método POST. La principal diferencia entre GET y POST estriba en que el método GET hace una sola llamada al servidor, solicitando una página y enviando algunos parámetros de datos en la propia petición. POST por el contrario realiza dos conexiones al servidor. En la primera solicita una URL y en la segunda envía los datos. Por GET lo máximo que se puede enviar son 2 kB de información, mientras que por POST no existe esta limitación. Para enviar datos al servidor mediante POST nuestro código AJAX sería similar al siguiente: http = getHttpRequest() http.onreadystatechange = finCarga; http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true) http.send(‘Parametro1=1234&Parametro2=5’);
Con esto no hemos ganado demasiado. Ahora se envían los datos por POST (sólo cambia el primer parámetro de open) pero los hemos tenido que introducir en el método send en lugar de en la propia URL. Esto sólo simularía el envío de parámetros mediante POST desde un formulario HTML, aunque por otro lado en ocasiones puede ser lo que queramos. Lo habitual sin embargo es que, en lugar de enviar parámetros, queramos enviar información pura y dura del tamaño que sea preciso, que es para lo que suele usarse POST. Esto se puede conseguir modificando ligeramente el código anterior para incluir una cabecera que indique al servidor que lo que le llega son, precisamente, datos (línea 3 del siguiente fragmento): http = getHttpRequest() http.onreadystatechange = finCarga; http.setRequestHeader(‘content-type’, ‘application/x-www-formurlencoded’); http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true) http.send(‘Aquí ahora mando la información que quiera al servidor’);
Con esto nuestro problema queda resuelto.
20 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
4.4.- Contenidos no actualizados debido a cachés Cuando se envía una petición HTTP es posible que, si la caché del lado servidor no está correctamente configurada, el navegador realice su propia caché local. Por lo tanto la próxima vez que realicemos una llamada a la misma URL, el navegador en lugar de hacerla sacará el mismo resultado anterior de esa caché local, y por lo tanto la llamada no llega al servidor jamás. O puede que exista un proxy-caché por el medio (Teléfonica por ejemplo las ha utilizado tradicionalmente en sus servicios de acceso a Internet) que almacena peticiones anteriores y por lo tanto obtenemos únicamente una copia, sin realizar la llamada al servidor real. Eso muchas veces es lo que querremos para ahorrar procesamiento y será maravilloso, pero lo habitual es que sea una maldición ya que evitará que obtengamos datos actualizados. A la hora de enviar datos por POST no hay problema porque no actúa nunca la caché. El problema, si se da, está en las peticiones GET, por otro lado las más habituales. Si el servidor tiene bien configurada la caché (es decir, indica cuándo caducan los contenidos o marcamos en IIS que éstos caduquen inmediatamente) no deberíamos experimentar fallos, salvando lo comentado respecto a los proxy-caché de algunos proveedores. Si queremos asegurarnos de que la petición va a llegar a su destino podemos hacer fundamentalmente dos cosas: 1. Agregar una cabecera que indique que se debe obtener el contenido siempre que éste sea posterior a una fecha, por ejemplo así: http.setRequestHeader(‘If-Modified-Since’, ‘Wed, 1 Jan 1972 00:00:00 GMT’);
Indicaremos siempre una fecha anterior a la actual como la del ejemplo y así siempre se pedirá la última versión al servidor. 2. Añadir un número aleatorio (o cadena) a la URL de cada petición. En este caso suele funcionar muy bien el agregarle una marca temporal, es decir, añadir a continuación la fecha y hora actuales, de modo que cada una de las peticiones que se hagan va a ser diferente y por lo tanto los caché que existan por el medio tienen que repetir siempre la petición. Por ejemplo: http.open(“POST”, “http://www.miserv.com/misdatos.aspx?pasacache=” + new Date().getTime(), true);
Se le añade un parámetro que lleva como valor la fecha y hora en formato numérico (es decir, un número muy largo y que varía varias veces cada milisegundo), por lo que es muy difícil que se den dos peticiones idénticas incluso a través de un proxy-caché. Además ese parámetro extra de nombre inventado que nosotros le añadimos no debería afectar en absoluto a la llamada puesto que no está siendo
Fundamentos de AJAX 21
tenido en cuenta por la aplicación. Esta segunda técnica es la más fiable, aunque un poco más tediosa de implementar.
5.- Devolución de información: JSON En el ejemplo anterior hemos hecho que la página del servidor devuelva ciertos valores que en este caso tenían formato XML, pero que podrían tener perfectamente otra configuración distinta, como por ejemplo simples valores separados por comas. Si bien esto puede ser suficiente en los casos más sencillos, en otras ocasiones necesitaremos manejar estructuras de datos más complejas. Dado que HTTP es un protocolo basado en texto, el recurso al que llamemos en el servidor debe devolver siempre texto (o sea, no puede ser una imagen o un archivo binario, que para transferirse se codifican de una forma especial -Base64- para convertirlos en texto). Este texto devuelto puede tener cualquier formato: texto plano, XML, código JavaScript o incluso HTML. En este último caso podemos obtener desde el servidor un contenido HTML completo que se debe escribir en una zona de la página (por ejemplo dentro de un
o un ). Sin embargo la mayor parte de las veces lo que tendremos que procesar es alguna estructura de datos. Ya hemos mencionado que la ‘X’ de AJAX significa XML, pues era el formato de moda a finales de los 90 y principios de los 2000 y se usaba para todo. Como se demostró en nuestro sencillo ejemplo este formato no es necesariamente el que se va a devolver. De hecho hoy en día el XML se usa muy poco a la hora de representar los datos textuales devueltos desde el servidor en páginas AJAX. El motivo de esto es principalmente que los datos representados con XML, si bien son ricos en estructura, hacen que el resultado devuelto ocupe mucho debido a las etiquetas de apertura y cierre de los diferentes nodos. Gracias al DOM es fácil procesar la información jerárquica que se representa mediante XML, aún así debería existir algún método más directo, más rápido y que ocupe menos ancho de banda. Como alternativa a XML surgió un nuevo formato programable llamado JSON (pronunciado “yeison”) que lo reemplaza con mucha ventaja en la mayor parte de los casos. JSON es el acrónimo de JavaScript Object Notation, y como su propio nombre indica permite representar objetos (en realidad estructuras complejas) en forma de código JavaScript que luego podemos evaluar. JSON tiene varias ventajas sobre XML, a saber: 1. Ocupa mucho menos al transmitirlo por la Red. 2. El acceso en el navegador a los elementos de datos representados es directo y sin necesidad de procesamiento farragoso usando el DOM o expresiones regulares, ya que se trata directamente de JavaScript que se puede interpretar. 3. Los datos pueden ir colocados en cualquier posición. Consideremos el siguiente código XML que representa los datos de un cliente:
22 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
José ManuelAlarcón AguínKrasis902 876 47537
Ahora consideremos la misma representación en JSON: {
Crearlo es muy fácil pues es sintaxis JavaScript normal. En www.json.org es posible encontrar una explicación completa de esta notación. La Wikipedia también tiene una información muy completa sobre ello. Como se puede comprobar ocupa menos que el XML equivalente, es igual o incluso más fácil de leer que éste, y permite usar datos nativos y no sólo cadenas para representar los valores. En estructuras de datos más complejas se puede apreciar más todavía el ahorro de datos que implica. De todos modos lo más espectacular de JSON es lo fácil que resulta usarlo. Basta con escribir lo siguiente: var cliente = eval(res);
Siendo ‘res’ el nombre de la variable que contiene el JSON obtenido del servidor. Es decir, lo único que hacemos es procesar la expresión JSON. Al hacerlo obtenemos en la variable ‘cliente’ un objeto cuyas propiedades son los datos que queremos manejar. De este modo lo único que tenemos que hacer para leerlos es escribir directamente expresiones como esta: alert(“El nombre de la empresa es “ + cliente.empresa);
Más fácil imposible. Nada de recorrer una jerarquía XML con el DOM o ir buscando nodo a nodo en el contenido. Se convierte en JavaScript puro y utilizable nada más llegar desde el servidor. El uso de JSON como formato de intercambio sólo tiene dos problemas aparentes. El primero de ellos es el más obvio: generar XML con C# o VB.NET es muy fácil pero generar JSON ha requerido tradicionalmente un cierto trabajo por nuestra parte. Para evitárnoslo las últimas versiones de .NET (3.5 o superior) incluyen la posibilidad
Fundamentos de AJAX 23
de que los servicios Web y WCF (Windows Communication Foundation) generen sus resultados directamente en JSON. Existen además bibliotecas especializadas en generar JSON si estamos interesados en hacerlo de manera manual. Por ejemplo, una muy famosa es Jayrock (http://jayrock.berlios.de) que permite convertir objetos .NET directamente a su representación JSON. El otro problema es tal vez menos evidente pero más importante: la seguridad. Dado que se usa una expresión ‘eval’ para convertir el texto en objetos JavaScript podría utilizarse de manera malintencionada para inyectar código peligroso en la página al efectuar la evaluación. Para evitarnos este peligro con código en el que no confiamos, en JSON.org tenemos un script (http://www.json.org/json.js) que extiende las cadenas de JavaScript para convertirlas a JSON verificando la sintaxis y evitando todo aquello que no sean datos. Si incluimos este Script en nuestra página en lugar de utilizar ‘eval’ podemos escribir: var cliente = res.parseJSON();
para obtener el mismo resultado. Además nos ofrece la funcionalidad inversa de la siguiente manera: var miCadena = cliente.toJSONString();
Esto nos puede servir para enviar datos JSON al servidor, para almacenar un objeto en una cookie, etc...
6.- En resumen En este capítulo hemos aprendido los fundamentos de AJAX así como los principales problemas que nos podemos encontrar al utilizarlo. La comprensión de todo ello nos va a resultar útil aunque no usemos estas técnicas de “bajo nivel” sino que recurramos a un kit especializado como ASP.NET AJAX, o cualquiera de las múltiples bibliotecas de JavaScript con soporte AJAX existentes en el mercado. Lo habitual será que siempre implementemos características AJAX en nuestras páginas usando alguna biblioteca especializada. En el mundo Microsoft emplearás con toda seguridad la parte cliente de ASP.NET AJAX, así como la biblioteca de código abierto JQuery, que se soporta oficialmente a partir de Visual Studio 2010. En el próximo capítulo vamos a estudiar las principales características de ASP.NET AJAX en el lado de servidor, que se traducen en mejoras en el lado del cliente.
capítulo
3
ASP.NET AJAX en el servidor Como hemos visto en el capítulo anterior, AJAX es un gran aliado a la hora de mejorar la usabilidad y la capacidad de respuesta de la interfaz de nuestra aplicación Web. Las técnicas AJAX nos evitan tener que refrescar la página completa cada vez que se genere un evento de servidor, lanzando las peticiones en segundo plano y refrescando sólo las partes de la interfaz que sean apropiadas en cada caso. Conseguir todo esto directamente con nuestro propio código JavaScript en el navegador puede convertirse en una tarea tediosa, que necesita bastante trabajo y es propensa a errores. Para evitarnos estos problemas y facilitarnos la tarea ASP.NET incluye una biblioteca especializada en creación de aplicaciones AJAX. Microsoft, en un alarde originalidad, llamó a esta biblioteca ASP.NET AJAX. Su nombre anterior, cuando estaba todavía en versión Beta, era “Atlas” y la verdad es que a casi todo el mundo le gustaba más :-) Nota: Como ya comenté en el capítulo de introducción, ASP.NET AJAX está incluido en.NET de manera nativa a partir de su versión 3.5 (y versión 2008 de Visual Studio) y en todas las versiones posteriores. En ASP.NET 2.0 la biblioteca se debe descargar e instalar por separado para poder utilizarla. Puedes encontrarla en www.asp.net/ajax. No hay soporte para AJAX en las versiones 1.0 y 1.1 de ASP.NET, ya completamente desfasadas.
La biblioteca de AJAX se divide en dos partes claramente diferenciadas pero que trabajan de manera conjunta para dotarnos de funcionalidad: · Biblioteca de servidor: contiene un conjunto de controles Web que abstraen de manera muy efectiva las tareas que deberíamos hacer normalmente en el cliente. Generan todo el código necesario para gestionar de manera asíncrona las llamadas al servidor y las actualizaciones. Los dos más importantes son el control ScriptManager y el UpdatePanel. En la figura se pueden observar los controles dentro del cuadro de herramientas de Visual Studio.
25
26 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Figura 1.- Controles de servidor para AJAX
· Biblioteca de lado cliente: se trata de una serie de scripts en JavaScript que extienden las capacidades de este lenguaje para facilitar la creación de código en el navegador (código de lado cliente). La parte servidora se basa en estas funciones para generar su funcionalidad. Ofrece, por ejemplo, capacidades avanzadas de orientación a objetos para JavaScript, creación de controles reutilizables, capacidad para crear scripts localizados, llamadas a servicios Web desde el cliente y, por supuesto, características para el control de las comunicaciones con el servidor. La funcionalidad de lado cliente es, en verdad, la parte más compleja de ASP. NET AJAX y le dedicaremos dos capítulos del libro. En este nos vamos a centrar en la funcionalidad generada desde el servidor.
1.- Un primer ejemplo: Mejora de una aplicación básica con AJAX Para empezar a abrir boca con ASP.NET AJAX vamos a reproducir el ejemplo sencillo del capítulo anterior de fundamentos usando ASP.NET. Veremos cómo se comporta al tratarse de una aplicación ASP.NET normal y luego lo fácil que es convertirlo en una aplicación AJAX. Nos servirá para conocer los fundamentos de la parte servidora de la API de AJAX. Crea un nuevo sitio Web con Visual Studio y en la página por defecto añade dos controles DropDownList, uno a continuación del otro separados con un par de espacios en blanco. En el primero de ellos habilita el “Auto postback” de modo que se genere automáticamente un evento SelectedIndexChanged en el servidor cada vez que se seleccione un elemento diferente de la lista. Usa la opción de editar elementos en su menú de tareas para añadir cuatro categorías: Revistas, Blogs, Empresas y Libros.
Figura 2.- El menú de tareas del control DropdownList
ASP.NET AJAX en el servidor 27
Ahora vamos a responder a su evento de selección para introducir en la segunda lista los elementos correspondientes a la categoría seleccionada. Haz doble clic en el control DropDownList1 para obtener la definición del manejador de eventos para su evento SelectionIndexChanged y escribe el siguiente código: Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1. SelectedIndexChanged Select Case DropDownList1.SelectedValue.ToLower() Case “revistas” DropDownList2.Items.Clear() DropDownList2.Items.Add(New ListItem(“dotNetMania”)) DropDownList2.Items.Add(New ListItem(“MSDN Magazine”)) DropDownList2.Items.Add(New ListItem(“CodeProject”)) Case “blogs” DropDownList2.Items.Clear() DropDownList2.Items.Add(New ListItem(“www.jasoft. org”)) DropDownList2.Items.Add(New ListItem(“www.geeks.ms”)) DropDownList2.Items.Add(New ListItem(“weblogs.asp. net”))
Case “empresas” DropDownList2.Items.Clear() DropDownList2.Items.Add(New krasis.com]”)) DropDownList2.Items.Add(New microsoft.com]”)) DropDownList2.Items.Add(New [www.plainconcepts.com]”)) Case “libros” DropDownList2.Items.Clear() DropDownList2.Items.Add(New castigo”)) DropDownList2.Items.Add(New soledad”)) DropDownList2.Items.Add(New End Select End Sub
ListItem(“Crimen y ListItem(“Cien años de ListItem(“El Quijote”))
Con esto tenemos suficiente para demostrar la diferencia entre una aplicación Web corriente y una con soporte para AJAX. Ejecuta la aplicación pulsando F5 (acepta el diálogo que te avisa que se va a modificar web.config para dar soporte a la depuración) y cuando se abra el navegador juega un poco con la interfaz cambiando la selección en la primera lista.
28 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Como puedes comprobar, cada vez que eliges un elemento nuevo en la lista 1, se produce un postback al servidor y al regresar la página, ésta se recarga y muestra los elementos apropiados en la segunda lista. Los efectos molestos de esta aplicación tan sencilla son fáciles de ver: 1. Durante la recarga de la página se produce un claro parpadeo, correspondiente al borrado de la página original y la subsiguiente recarga de ésta con los nuevos valores. Si usas Internet Explorer además oirás un sonidito (como un “clac”) en cada recarga. 2. La primera vez que haces una selección normalmente la página tarda un poco más de lo habitual en ejecutarse. Durante quizá medio segundo el usuario no tiene ni idea de si la acción que ha llevado a cabo en la interfaz (en este caso seleccionar una categoría de la lista) ha tenido efecto o no. En algunas aplicaciones reales en las que el código es más complicado y puede que haya demoras de E/S debido a accesos a bases de datos, a disco o a redes congestionadas, las esperas para recibir las respuestas a eventos de servidor pueden tardar incluso varios segundos, durante los cuales el usuario no tiene ni idea de qué está pasando. 3. Un efecto muy desagradable de los postback y del que muchos programadores no se percatan es el de las entradas indeseadas en el historial de navegación. En nuestro ejemplo cada vez que seleccionamos un elemento de la lista y se provoca un postback aparece una nueva entrada en la historia del navegador. Si el usuario pulsa la flecha para ir a la página anterior irá pasando por cada una de las selecciones que haya hecho en la lista. Sin embargo el usuario ha tenido la sensación de estar trabajando todo el tiempo en la misma página. Para él o ella debería ser transparente el hecho de que nosotros por debajo estemos reenviando la página. Cuando pulsa el botón de volver a la página anterior lo que un usuario espera es realmente volver a la “pantalla” en la que estuviese previamente, no a los sucesivos pasos de trabajo en la misma página actual. Por supuesto existen excepciones y a veces será necesario todo lo contrario: que incluso en aplicaciones AJAX se creen algunas entradas en el historial. Lo trataremos en otro capítulo con detalle. Vamos a retocar nuestro ejemplo sacándole partido a los controles AJAX que se pueden ver en la primera figura. De momento no explicaremos sus funciones y nos limitaremos a añadirlos sin más. En los siguientes epígrafes analizaremos con detalle cada uno de ellos. Abre la superficie de diseño de la página ASPX y desde el panel de herramientas añade un control ScriptManager que encontrarás en el grupo de Extensiones AJAX. Asegúrate de que el control se encuentra como primer elemento de la página, es decir, arrástralo delante del primer control DropDownList que teníamos anteriormente. Ahora arrastra, justo a continuación del anterior, un control UpdatePanel. Al hacerlo verás que aparece una nueva área vacía de poca altura, que es la única pista visual de que se ha arrastrado este último control.
ASP.NET AJAX en el servidor 29
Selecciona cada uno de las dos listas desplegables y arrástralas dentro del UpdatePanel. Asegúrate de que quedan ubicadas dentro de éste. Como se observa en la figura, el recuadrado sutil del control UpdatePanel nos permite ver sus límites y saber si los controles se encuentran realmente dentro de él.
Figura 3.- Nuestra aplicación preparada para AJAX con sólo arrastrar dos controles.
Podríamos haber incluido los controles dentro del UpdatePanel también desde el código fuente de la página, en lugar de arrastrándolos, si cambiamos a la vista HTML y nos aseguramos de que sus etiquetas están encerradas dentro de las etiquetas de tipo del panel, como se ve en la figura 4.
Figura 4.- Las etiquetas de los controles en el código fuente se encuentran dentro del UpdatePanel.
Ya está. No es necesario hacer nada más que arrastrar este par de controles. Ejecuta de nuevo la aplicación. Ahora verás que al seleccionar una categoría en la primera lista la segunda se recarga enseguida con los valores apropiados sin recargar la página. El parpadeo ha desaparecido y en la historia del navegador no aparecen nuevas entradas. Más sencillo imposible.
30 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Nota: La mayor parte de la gente se queda con esta idea de sencillez que el control UpdatePanel nos brinda. Si bien es cierto que gracias a él la creación de aplicaciones AJAX se simplifica en gran medida, también es verdad que hay multitud de pequeños detalles a tener en cuenta sobre rendimiento y optimización que se le escapan al que simplemente se queda en la superficie. Ello provoca que, luego en producción y sometidas a mucha carga, algunas aplicaciones AJAX ofrezcan muchos problemas de rendimiento, ya que están hechas sin tener un buen conocimiento de lo que se hacía. En este capítulo trataremos de comentar conceptos e ideas clave que nos ayudarán a comprender mejor toda esta tecnología. Por lo de pronto, si ya has leído el capítulo anterior de fundamentos, ya tienes más herramientas que la mayoría de los programadores ASP.NET AJAX que encontrarás por ahí.
2.- Postbacks parciales y repintados parciales de página El proceso que se lleva a cabo por debajo en las páginas AJAX como la del ejemplo anterior se denomina postback con repintado parcial de página. El funcionamiento de una página ASP.NET AJAX no difiere demasiado del de una página ASP.NET normal y corriente. Ello es una gran ventaja puesto que no nos obliga a aprender conceptos diferentes o a tratar a las páginas de un modo distinto por el hecho de usar esta tecnología. Lo que ocurre durante un repintado parcial de página (en el que sólo una parte de su interfaz se modifica) es en realidad un postback completo de la misma. La diferencia con una página normal estriba en que éste se realiza de manera asíncrona y “por debajo” usando JavaScript para solicitar la página y procesar sus resultados. La otra diferencia es que en lugar del contenido completo se devuelve sólo el HTML que compete a los contenidos del UpdatePanel que se va a actualizar. El código JavaScript generado automáticamente por el ScriptManager se encarga de gestionar ese resultado y repintar dinámicamente sólo la parte apropiada de la interfaz, así como actualizar los datos de estado de los controles para subsiguientes recargas de la página. En el servidor todo esto significa que cuando se produce un evento en un control, aunque se vea afectada sólo una pequeña parte de la página AJAX, en realidad se está procesando un postback completo, con todos sus eventos de ciclo de vida. Es más, se regenera totalmente el árbol de controles de la página, se lanzan todos los eventos de ciclo de vida de éstos, se envía y recibe todo el Viewstate, etc... La única diferencia es que se devuelve sólo el HTML de las partes de la página que se están modificando dentro del UpdatePanel. Olvidar esto es un error muy común que cometen los programadores, que no se dan cuenta de que, aunque se vaya a actualizar sólo un pequeño control, en realidad se está enviando el estado de todos los contenidos en la página (y hay ViewStates que ocupan mucho), se ejecutarán todos los eventos de página tales como Page_Load y los eventos de ciclo de vida de todos los controles aunque no intervengan en la acción. O sea, exactamente igual que en una página normal.
ASP.NET AJAX en el servidor 31
Esto tiene mucha importancia ya que podemos tener una página enorme que se construye a partir de un proceso complejo y costoso (por ejemplo obtener mucha información de una base de datos o un servicio Web remoto), en la que hemos añadido un UpdatePanel en el que se modificará solamente una pequeña cantidad de información como respuesta a una acción. Podemos pensar que el refresco de ese panel será una operación ágil y muy poco costosa ya que los datos recibidos son minúsculos. Es muy fácil que nos equivoquemos y la actualización parcial aparentemente inocente lleve mucho tiempo y sea muy costosa en términos de red y de proceso en el servidor si no hemos sido cuidadosos en nuestro código. El nombre que se le suele otorgar a los postback de páginas AJAX tampoco ayuda demasiado a aclarar este concepto, ya que normalmente se les denomina en artículos y documentaciones como “postback parciales”, para distinguirlos de los “postback normales” que se ejecutan cuando no hay funcionalidad AJAX. En mi opinión deberían llamarse “postback asíncronos” o, directamente, no distinguir entre unos y otros y sólo hablar de actualizaciones parciales, que es lo que realmente ocurre. No te dejes engañar por el nombre cuando leas esta denominación y recuerda que los postback son siempre completos. La primera consecuencia de esto es que, aunque parezca que no lo necesitamos, debemos seguir comprobando en la carga de la página si nos encontramos en un postback o no, para lo cual usaremos la propiedad IsPostBack de la página. Así evitaremos operaciones de inicialización innecesarias en cada recarga, como haríamos en cualquier página normal. Si por el motivo que sea necesitamos saber si nos encontramos dentro de un postback conducente a una actualización parcial de la página, o sea de un postback asíncrono de AJAX, podemos saberlo consultando la propiedad IsAsyncPostback del control ScriptManager, que tomará el valor True cuando este sea el caso.
3.- El control ScriptManager El núcleo sobre el que se sustenta toda la funcionalidad AJAX de ASP.NET es el control ScriptManager. Como sabemos, la funcionalidad AJAX de cualquier aplicación Web (sea con .NET o no) se basa en la interacción entre el navegador y el servidor, la cual se gestiona a través de código JavaScript que realiza llamadas a recursos remotos y maneja las respuestas obtenidas para actualizar la interfaz de usuario. El control ScriptManager se encarga de generar todo el código JavaScript necesario para sustentar la funcionalidad AJAX en el navegador. Como veremos al analizar la parte cliente de AJAX, este código JavaScript se ofrece en forma de librerías especializadas que habilitan todas las características especiales de AJAX mencionadas al principio del capítulo: llamadas a servicios Web, funciones de comunicaciones... y por supuesto, como funcionalidad estrella, la capacidad de efectuar postbacks en segundo plano y hacer el repintado parcial de las páginas, que es lo que acabamos de experimentar en el ejemplo.
32 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Siempre que deseemos utilizar funcionalidad AJAX debe existir un control ScriptManager en la misma. También se pueden utilizar dentro de controles de usuario y en Master Pages, aunque luego veremos las particularidades de usarlos ahí. Entre las funciones de este control están: •
Generar los Scripts necesarios para sustentar la funcionalidad AJAX y las extensiones de JavaScript para desarrollo en el lado cliente.
•
Registrar nuestros propios scripts en el cliente y el envío de estos dinámicamente durante los repintados de página.
•
Proporcionar acceso a servicios Web en el servidor.
•
Soporte para localización y globalización de código de lado cliente en función de los ajustes culturales del navegador.
•
Dar soporte a los servicios de Membership, Roles y Profile de ASP.NET para hacer uso de estos directamente desde el lado cliente, con JavaScript.
•
Permitir la creación de controles y comportamientos basados en JavaScript (extensiones) que otorgan de funcionalidad extra a los controles HTML normales del navegador y a los controles Web de ASP.NET.
Dado que, como vemos, este control tiene muchas funcionalidades aparte de la de permitir el repintado parcial de página, si no queremos utilizar esta característica y sólo estamos interesados en hacer otras cosas con él en el lado cliente, podemos desactivarla estableciendo a False su propiedad EnablePartialRendering. Por otro lado es posible que en navegadores muy antiguos la funcionalidad de repintado parcial no esté soportada. En esos casos el control ScriptManager se degrada elegantemente y deja que la página se comporte de la manera habitual, con postbacks síncronos y repintado completo de la página. Podemos determinar esta situación consultando su propiedad SupportsPartialRendering.
4.- El control UpdatePanel Este control es una abstracción que nos permite indicar qué partes de nuestra interfaz de usuario queremos que tengan la capacidad de actualizarse parcialmente, de forma independiente al resto de la página. Como hemos visto ya (figura 4) el marcado del control tiene una pareja de etiquetas que definen una zona contenedora de otros controles. Todos los controles definidos dentro de esta zona pueden participar de un repintado parcial de la página. Esta región comprendida entre ambas etiquetas se corresponde en el diseñador visual de Visual Studio con el área contenedora del UpdatePanel a la que podemos arrastrar los controles. Además de añadir controles en esta zona visualmente o incluyendo sus etiquetas en el marcado de la página, es posible añadirlos (o retirarlos) también dinámicamente
ASP.NET AJAX en el servidor 33
mediante código. De esta forma se pueden variar en tiempo de ejecución los contenidos de la zona de repintado parcial. Para ello se debe manipular la colección Controls de la propiedad ContentTemplateContainer del UpdatePanel, así por ejemplo: Dim Contenedor As Control = UpdatePanel1. ContentTemplateContainer Dim lbl As New Label() lbl.Text = DateTime.Now.ToString(“hh:mm:ss”) Contenedor.Controls.Add(lbl)
5.- Modos de actualización parcial En una página podemos tener tantos UpdatePanel como necesitemos, para actualizarlos parcialmente de forma individual, coordinada o todos a la vez. Un control UpdatePanel puede contener a su vez a otros UpdatePanel, generándose una jerarquía de actualizaciones parciales que sigue determinadas reglas. Lo más importante a tener en cuenta durante un repintado parcial es qué lo provoca y qué efectos tendrá sobre los diferentes UpdatePanel que tenemos repartidos por la página. Cuando un UpdatePanel se encuentra dentro de otro, el del interior se actualiza siempre que lo hace su contenedor. Cuando el UpdatePanel es independiente (la situación más habitual) su comportamiento durante el repintado parcial de la página viene determinado por el valor de su propiedad UpdateMode, que puede tomar los siguientes valores: •
Always: si la propiedad UpdateMode tiene este valor el contenido se refrescará después de cualquier postback que se realice a la página, sea éste generado específicamente para este control o no. Esto es importante ya que este es el valor por defecto de la propiedad. Si no somos cuidadosos y tenemos varios UpdatePanel en la página veremos que, en muchas ocasiones, se actualizan zonas que no contábamos que se iban a refrescar, ya que estábamos actuando realmente en otro lugar.
•
Conditional: el contenido del panel sólo se repintará si el control que lanza el postback es uno de los disparadores para el UpdatePanel (ahora veremos qué es esto) o bien cuando llamamos explícitamente al método Update del panel desde un evento de servidor. Por supuesto, como ya se ha comentado, se repintará también siempre que esté contenido dentro de otro panel que se ha repintado.
Jugando con esta propiedad podemos obtener un gran control sobre en qué momento y ante qué eventos se refrescará el contenido de una zona concreta de la página.
34 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
6.- Disparadores De manera predeterminada los controles que están dentro de los UpdatePanel provocarán un repintado parcial de la página, mientras que los controles que se encuentran fuera de éstos provocarán postbacks normales con la recarga completa de la misma. Aunque este comportamiento pueda resultar adecuado para muchos casos, normalmente vamos a necesitar un mayor control sobre las situaciones que provocan la recarga de las páginas y qué zonas de ésta se ven afectadas. Por ejemplo, si tenemos un control que queremos que provoque el refresco parcial de una parte de la página alejada físicamente de donde está éste ubicado, tenemos dos opciones: 1. Incluir el control y la zona que queremos actualizar dentro del mismo UpdatePanel. Esto probablemente nos obligue a incluir muchos otros controles en el panel, que no nos interesa en absoluto que participen en un repintado parcial, puesto que no van a cambiar o queremos que se vean afectados por otros eventos diferentes. Llevado al extremo algunos programadores con poco conocimiento, lo que hacen es ¡rodear con un UpdatePanel todos los controles de su página!. Esto es obviamente una solución muy ineficiente y como se suele decir coloquialmente es “matar moscas a cañonazos”. 2. Definir qué controles actuarán como disparadores de cada panel, y por lo tanto lanzarán postbacks asíncronos conducentes a refrescar el contenido del mismo. Estos controles no tienen porqué estar dentro de un UpdatePanel, sino en cualquier lugar de la página. Esta es la solución inteligente y optimizada. Para implementar esta funcionalidad se definen los disparadores o Triggers. Por defecto todos los controles contenidos dentro de un UpdatePanel se comportan como disparadores de éste. Es por ello que en el ejemplo anterior no hemos tenido que hacer nada para que funcionara el repintado parcial de la segunda lista desplegable. Este comportamiento se controla a través de la propiedad ChildrenAsTriggers del control UpdatePanel. Si la establecemos como False entonces los controles que contiene no serán disparadores y tendremos que definirlos a mano. El uso más común de los disparadores es el de asociar eventos de servidor de controles externos a un panel para provocar repintados parciales de éste. Visual Studio nos brinda un diálogo especial que facilita su definición. Vamos a verlo en funcionamiento con un ejemplo. Añade al proyecto de prueba una nueva página “Triggers.aspx”. Arrastra sobre ella un ScriptManager y un UpdatePanel. Dentro de este último coloca una etiqueta. Ahora, fuera del panel, justo debajo, inserta un botón. El aspecto final debería ser como el de la figura 5:
ASP.NET AJAX en el servidor 35
Figura 5.- Aspecto de nuestra aplicación de prueba
En el manejador del evento de pulsación del botón simplemente vamos a mostrar la hora actual dentro de la etiqueta: Label1.Text = DateTime.Now.ToString(“HH:MM:ss”)
Ahora ejecuta la página. Al pulsar sobre el botón cabría esperar que la etiqueta mostrase la hora sin necesidad de recargar la página. Sin embargo comprobarás que en lugar de eso se provoca un postback y un refresco de toda la página. El motivo es que el botón, al estar fuera del UpdatePanel no es automáticamente un disparador para su repintado parcial. Vamos a ponerle remedio. Selecciona el control UpdatePanel y en sus propiedades (pulsa F4) verás que existe una colección Triggers.
Figura 6.- Colección de disparadores del panel
36 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Utilizando el botón con tres puntos situado a la derecha se abre el diálogo de gestión de disparadores, que podemos ver en la figura 7.
Figura 7.- Editor de disparadores de un panel
El botón de añadir nos permite asignar uno o varios controles como disparadores del panel, es decir, como controles que van a provocar un repintado parcial del mismo. La rejilla de propiedades de la derecha nos facilita la selección de controles y eventos. En nuestro ejemplo seleccionaremos como control disparador el botón de actualizar la hora (cmdHora), y dentro de los posibles eventos de éste, el evento Click, es decir su pulsación. Se podría hacer que el disparador fuese cualquier otro evento, e incluso que varios eventos de un mismo control sirvan como disparadores del panel. Una vez añadido nuestro botón como disparador, acepta para cerrar el diálogo y vuelve a ejecutar la aplicación. Comprobarás como, ahora sí, al pulsar el botón la hora se visualiza en la etiqueta sin necesidad de refrescar la página por completo. Si nos fijamos en el código de marcado de la página, veremos que es posible definir los disparadores del panel manualmente de forma sencilla, usando la etiqueta :
ASP.NET AJAX en el servidor 37
Fíjate en que lo único que necesitamos es establecer las dos propiedades del disparador como unos simples atributos de texto, indicando el nombre del control y el del evento a asociar. Los disparadores asíncronos como este se pueden utilizar también para lanzar actualizaciones parciales a partir de controles de dentro del panel cuando ChildrenAsTriggers es falso. También para forzar la actualización de un panel padre desde un control ubicado en un panel anidado dentro de él. Por defecto los controles que están dentro de los UpdatePanel harán un refresco parcial asíncrono de la página. En ocasiones sin embargo es posible que necesitemos que, si bien la mayoría de los controles del panel se comporten así, uno o varios de ellos provoquen un refresco completo de la página. Por ejemplo cuando un cambio dentro del panel afecta a datos que se están visualizando fuera del mismo. Para ello existen un tipo especial de disparadores que se llaman simplemente PostBackTriggers. Si te fijas bien en la figura y en el listado anteriores verás que el disparador que hemos utilizado es de tipo AsyncPostBackTrigger porque desencadena un postback asíncrono. En la figura 7 se ve como al desplegar el botón de añadir disparador hay un tipo adicional sin el prefijo Async, siendo simplemente PostbackTrigger. Este tipo de disparadores te dejan seleccionar un control contenido en el UpdatePanel, y al asociarlo conseguiremos generar un postback con recarga completa de la página aunque la propiedad ChildrenAsTriggers tenga el valor por defecto de True.
7.- Indicación de progreso de las llamadas asíncronas En los ejemplos que hemos hecho, las tareas realizadas como respuesta a los eventos de servidor eran triviales para poder centrarnos en la funcionalidad AJAX únicamente. En una aplicación real tendremos tareas de todo tipo que pueden ser más o menos costosas. Por ejemplo ciertas labores de acceso a datos pueden tardar bastante en realizarse, y si le sumamos la velocidad de la red que puede ser baja, una carga importante de usuarios en el servidor, etc... es posible —y hasta frecuente— que algunas de las llamadas asíncronas que se realizan al servidor tarden más de la cuenta en regresar. Mientras el navegador espera la respuesta de una llamada asíncrona para repintado parcial, el usuario no tiene pista alguna de lo que está pasando. En cuanto pase medio segundo y no haya un efecto visible en la página la mayoría de los usuarios
38 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
se empezarán a preguntar si ha fallado algo. Incluso muchos se pondrán a pulsar los botones nuevamente varias veces para comprobar si responde, agravando el problema pues estarán lanzando más peticiones al servidor. Por eso, salvo en los casos más triviales, es importante utilizar algún tipo de indicador visual para los usuarios que aparezca en pantalla mientras dura la actualización (el viaje petición-respuesta-procesamiento desde el navegador al servidor y vuelta otra vez). Pero tampoco es recomendable que aparezca inmediatamente pues si la llamada es muy rápida se verá casi como un parpadeo que también desconcentrará al usuario. Para evitarnos la tarea de tener que programar algo así por nosotros mismos, ASP.NET AJAX ofrece un control llamado UpdateProgress que sirve precisamente para eso. Puedes verlo en la lista de controles de la figura 1. Al arrastrarlo sobre la superficie de nuestra página ASPX se mostrará como un control contenedor, de forma que podremos colocar dentro de él los elementos que deseemos que aparezcan durante una actualización asíncrona de la página. Aunque hay quien prefiere un espartano cartel de texto (mira sino los indicadores de progreso que muestra GMail, que son unas simples letras sobre fondo granate), lo más habitual es usar un gráfico animado en formato GIF que muestre una sensación de progreso mientras carga la página por debajo. En la página www.ajaxload.info dispones de una interesante utilidad gratuita que te permite generar online muchos gráficos de progreso diferentes. Simplemente escoges el tipo de gráfico entre una amplia lista, los colores y si quieres que sea transparente o no y el programa genera el gráfico para ti y te permite descargarlo.
Figura 8.- Utilidad online para generar gráficos de progreso AJAX
ASP.NET AJAX en el servidor 39
Para probar su funcionamiento vamos a añadir uno de estos controles a la página, justo a continuación del ScriptManager. Genera en la ajaxload.info un gráfico que te guste para indicar el progreso, grábalo a disco en el proyecto, refresca el explorador de soluciones y desde él arrástralo dentro del control de progreso. Selecciona el control UpdateProgress y ve a sus propiedades. Establece la propiedad AssociatedUpdatePanelID con el nombre del UpdatePanel, y su propiedad DynamicLayout como False, tal y como se ve en la figura 9.
Figura 9.- Propiedades del control UpdateProgress
Con estas propiedades lo que conseguiremos es que el indicador de progreso se muestre automáticamente durante las actualizaciones parciales del UpdatePanel1, y que el espacio que ocupa en la página se conserve incluso cuando está oculto. Si establecemos DinamicLayout como True, que es su valor predeterminado, cuando el indicador aparezca reclamará su sitio en la página (ya que antes no ocupaba nada), desplazando al resto de los controles hacia abajo, lo que puede crear un efecto feo si no lo tenemos en cuenta. Con el valor que le hemos dado, el espacio que ocupa está reservado y al aparecer no provoca desplazamiento alguno. La representación en marcado del control UpdateProgress es como la siguiente:
Lo que está dentro de sus etiquetas (en la superficie contenedora si lo vemos en modo diseño) se convierte en tiempo de ejecución en el HTML correspondiente encerrado dentro de una capa con la etiqueta
.
40 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Todavía queda por ver una propiedad muy importante: DisplayAfter. En ella indicaremos el tiempo en milisegundos que tardará el indicador en aparecer si la operación asíncrona no ha terminado. Por omisión este tiempo es de 500 milisegundos. Así, si la operación de postback tarda más de medio segundo en finalizar, aparecerá el indicador automáticamente para que el usuario sepa que el proceso está funcionando y que tardará un poco. Al terminar la operación se ocultará también de manera automática. Para probar el indicador en nuestro ejemplo sólo nos resta simular el efecto de una operación lenta en el servidor. Así que en el evento de pulsación del botón añadiremos una línea que hará que se bloquee la ejecución durante dos segundos justo antes de actualizar la hora en la etiqueta: System.Threading.Thread.Sleep(2000)
Ahora ejecuta la aplicación y pulsa el botón para mostrar la hora. ¡Eh! ¿Qué pasa? No se está mostrando el indicador de progreso. La cosa no funciona... El motivo es que en este caso nuestro disparador era un elemento externo al UpdatePanel (el botón está fuera y lo asociamos con un Trigger, si recuerdas). Cuando el control de progreso se asocia a un UpdatePanel concreto mediante su propiedad AssociateUpdatePanelID, no es capaz de detectar el comienzo y finalización de los postback generados por disparadores externos al panel. Ello se debe a que el JavaScript asociado lo que hace es recorrer la jerarquía de elementos DOM que hay bajo el UpdatePanel que debe controlar, para encontrar qué control ha lanzado el repintado. Como un disparador externo no está en esa jerarquía no es capaz de encontrarlo y por lo tanto el progreso no se muestra. No tendríamos problema si el botón estuviese dentro del panel. Para solucionarlo hay una acción muy simple que podemos llevar a cabo: eliminar la propiedad AssociatedUpdatePanelID. Si tenemos en la página un control de indicación de progreso que no está asociado a un panel, lo que conseguimos es un indicador de progreso universal que se mostrará para cualquier actualización parcial que se haga en la página. Si ahora ejecutas de nuevo la página verás que ya se comporta como era de esperar y el gráfico animado de progreso aparece al cabo de medio segundo, ocultándose automáticamente al final. De hecho es muy habitual tener un único control de indicación de progreso en la página que sirva para el proceso de repintado de cualquiera de los paneles contenidos en ésta.
8.- Refrescos parciales periódicos Otro de los controles de AJAX disponibles es el control Timer. Éste nos permite lanzar automáticamente procesos de actualización parcial con una frecuencia periódica y sin que sea necesaria acción alguna por parte de un usuario. Se puede utilizar para actualizar los contenidos de un UpdatePanel o por el contrario actualizar la página completa. Otra aplicación interesante es la de ejecutar
ASP.NET AJAX en el servidor 41
periódicamente código en el servidor aunque no sirva para actualizar nada visible en la interfaz. Es un control dependiente de AJAX y aunque se use para esto último va a requerir siempre la presencia de un ScriptManager en la página (tampoco es la mejor forma de hacer algo así, todo hay que decirlo). La única propiedad interesante de este control es Interval. A través de ella se establece el número de milisegundos que servirán como periodicidad del temporizador. Por defecto el periodo es de un minuto (60.000 segundos). No conviene usar un valor demasiado bajo para Interval ya que correremos el riesgo de saturar el servidor con el número de peticiones muy seguidas que se generarán. Cada vez que se cumple el tiempo indicado en el intervalo se lanza un evento de servidor para el temporizador. Este evento se denomina Tick. Como cualquier otro evento de servidor (como el clic de un botón o cualquier otro), éste provocará un postback de la página. Y al igual que con cualquier otro control, dependiendo de donde esté colocado dentro de la página se producirá un refresco parcial o total de la misma. Así, si el temporizador está dentro de un UpdatePanel con ChildrenAsTriggers= True (valor por defecto y el más habitual), el resultado será que se refrescan los contenidos del panel asíncronamente con la periodicidad especificada. Si lo colocamos fuera de un UpdatePanel se producirá un refresco completo de la página. Por supuesto podemos asignarlo como un disparador para uno o varios UpdatePanel, en cuyo caso lo situaremos fuera y nos servirá para hacer que se refresquen parcialmente cada cierto tiempo. Si tenemos varios temporizadores en la página y todos tienen el mismo intervalo es mucho más eficiente colocar uno sólo, fuera de todos los paneles, y asignarlo como disparador para refrescarlos a todos. Otra cosa a tener en cuenta es que para actuar sobre los controles de la página durante el evento del servidor, es indistinto hacerlo en el evento Tick del control o bien directamente en el evento Load de la página, ya que como sabemos durante el postback (aunque sea asíncrono) la página ejecuta su ciclo de vida normal. En el código descargable he incluido una página de ejemplo de uso del temporizador que se limita a actualizar la hora en una etiqueta cada segundo, consiguiendo un reloj en funcionamiento. Es evidente que algo tan sencillo es muy fácil de conseguir sin más que un poco de JavaScript en el cliente, así que hacerlo con un temporizador es un verdadero derroche de recursos y, como decía antes, “matar moscas a cañonazos” :-), pero nos sirve para ilustrar su funcionalidad. Sólo hay una última cosa importante que debemos tener en cuenta si la precisión del intervalo es importante para nosotros, y es que los tiempos pueden cambiar ligeramente en función de si el temporizador está dentro de un UpdatePanel o fuera de él. Si el Timer está fuera de cualquier UpdatePanel actuando como disparador de alguno de ellos, su periodo de refresco se recarga automáticamente en cuanto lanza el evento de servidor, sin esperar a que éste regrese, ya que el código de la página que le afecta no se modifica. Sin embargo cuando lo colocamos dentro de un panel, al refrescar su contenido, se regenera el código del temporizador y el tiempo comienza a contar de nuevo. Esto ocurre tras haber regresado del postback asíncrono. Por lo tanto hay una diferencia con el intervalo de la otra situación que será tanto mayor cuanto más tarde en procesarse la llamada asíncrona al servidor.
42 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Debes tenerlo en cuenta porque es una sutil diferencia pero puede ser importante en algunas aplicaciones.
9.- Unicidad del ScriptManager Al trabajar con los controles de servidor de ASP.NET AJAX hay que tener en cuenta una cosa muy importante, y es que cuando digo que debe existir un control ScriptManager en la página, me estoy refiriendo a que debe haber uno y sólo uno en todo el árbol de controles de la misma. Si intentamos añadir más de un control de este tipo a la página el compilador no se quejará, pero recibiremos un error en tiempo de ejecución como el de la figura.
Figura 10.- No es posible tener más de un control ScriptManager en una página
Esto tiene más importancia de la que parece a primera vista y es que afectará a la forma en la que podremos reutilizar algunas partes de nuestra aplicación. Por ejemplo, si tenemos una o varias Master Pages deberemos decidir si el control ScriptManager se colocará en la MP o bien tendremos que colocarlo en cada una de las páginas que hagan uso de funcionalidad AJAX. Lo que no podemos es tenerlo en los dos sitios a la vez ya que en tiempo de ejecución, al fusionarse los controles de la página plantilla y de la página funcional, nos encontraremos con dos controles y se producirá un error. Lo mismo ocurre si encapsulamos funcionalidad AJAX dentro de un control de usuario y en éste incluimos el ScriptManager. Si luego lo arrastramos sobre una página que ya tenga su propio ScriptManager se producirá un conflicto y la página no funcionará. En general si nuestra aplicación hace un uso amplio de las funcionalidades AJAX, como es cada vez más habitual, lo más recomendable es incluir el ScriptManager directamente en la plantilla (Master Page). Así estará disponible para todas las páginas hija que hagan uso de la misma. Si alguna de las páginas hija no utiliza refrescos parciales siempre podemos desactivar esta característica obteniendo una referencia al control durante la carga y usando su propiedad EnablePartialRendering, que ya hemos visto:
ASP.NET AJAX en el servidor 43
Dim sm As ScriptManager = ScriptManager.GetCurrent() sm.EnablePartialRendering = false
El método estático GetCurrent de la clase ScriptManager nos permite obtener una referencia a la instancia actual que se esté usando en la página, independientemente de que esté ubicada en la propia página, en un control o en la Master Page.
10.- el control ScriptManagerProxy Como hemos comentado (y veremos con detalle luego) el ScritpManager ofrece soporte para multitud de cuestiones más allá del repintado parcial de las páginas. Si incluimos uno de estos controles en una Master Page y luego requerimos servicios adicionales del mismo en una de sus páginas hijas, tenemos un problema. Si por ejemplo, queremos añadir una referencia a un script o a un servicio Web que es de uso específico de la página actual, dado que no podemos incluir otro control ScriptManager para hacerlo ¿cómo procederemos?. Una posible opción sería incluirlo en el que está en la Master Page, pero así tendríamos que incluir todos los posibles servicios en la MP y ponerlos a disposición de todas las páginas, los necesiten o no, lo cual es muy ineficiente y propenso a errores. Para solucionar este problema existe el control ScriptManagerProxy. Este control, disponible también en el panel de herramientas (ver figura 1), se arrastra a la página como otro cualquiera y actúa como un apéndice del Script Manager único que está en la Master Page para añadirle las características adicionales que necesitemos. Por ejemplo, este fragmento muestra la sintaxis de uno de estos controles que se usa para incluir en la página, en la sección <scripts> (luego la estudiaremos), un código de Script específico que se necesita para dotarla de alguna funcionalidad de lado cliente:
Este tipo de proxy se puede utilizar también en controles de usuario que luego van a ser utilizados en páginas que ya tiene un ScriptManager, bien porque lo incluyan directamente, bien porque lo tenga la Master Page correspondiente. Debes recordar bien este control porque lo usarás mucho más de lo que te imaginas en cualquier aplicación grande.
11.- Gestión de errores AJAX En condiciones normales todo va a funcionar de maravilla. Pero la dura realidad es que siempre, en algún momento, pasa algo que hace que las cosas fallen. Si durante un postback asíncrono se produce un error en el servidor ¿cómo podemos gestionarlo?.
44 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Vamos a crear una pequeña aplicación de ejemplo que produzca errores para ver cómo podemos gestionarlos. Crea una nueva página en el proyecto, “ErroresAjax.aspx”, y añádele un ScriptManager, un UpdatePanel y dentro de éste un simple botón con el texto “Provocar Error”. En el manejador del evento click de este botón lanza una excepción así: Throw New Exception(“Error provocado a mano!!”)
Ahora ejecútala pulsando CTRL + F5 para evitar que se trabaje en modo depuración, pues en ese caso saltaría un punto de interrupción en Visual Studio y no verías cómo funciona en la realidad, cuando esté en producción. Verás que el error en el lado servidor se traduce en el navegador en forma de un error de JavaScript. Si tienes la depuración de JavaScript activada verás un mensaje como el de la figura 11. Como se puede comprobar en dicha figura, el mensaje descriptivo del error original se visualiza en el error de JavaScript también.
Figura 11.- Error de JavaScript provocado en el servidor
En este caso el error, al ser manual, tiene una descripción amigable y neutra, y no pasa nada porque se vea. Sin embargo esta situación no es la más recomendable. Una de las reglas básicas de seguridad de aplicaciones dice que no se debe dar información técnica de errores a usuarios finales. Generalmente los errores que se producirán en el servidor incluyen en su descripción detalles internos de la aplicación, por ejemplo nombres de campos en bases de datos o rutas en disco, que podrían dar información valiosa a posibles atacantes. Por este motivo se recomienda gestionar siempre los errores y devolver al usuario un mensaje más amigable, sin dar detalles internos. Para ello el control ScriptManager nos ofrece el evento AsyncPostBackError, en el que podremos gestionar cualquier tipo de excepción que se produzca durante un postback asíncrono.
ASP.NET AJAX en el servidor 45
Figura 12.- Evento para gestión de errores en postbacks asíncronos.
Como segundo argumento de este evento tenemos un objeto de tipo AsyncPostbackErrorEventArgs. Su única propiedad interesante, Exception, nos permite acceder a la excepción producida y trabajar con ella para actuar en consecuencia. En nuestro ejemplo vamos simplemente a poner un mensaje más amigable independientemente de cuál sea la excepción producida: Protected Sub ScriptManager1_AsyncPostBackError(ByVal sender As Object, ByVal e As System.Web.UI.AsyncPostBackErrorEventArgs) Handles ScriptManager1.AsyncPostBackError ScriptManager1.AsyncPostBackErrorMessage = “Se ha producido un error en la operación. Si persiste contacte con el administrador” End Sub
Utilizamos la propiedad AsyncPostBackErrorMessage del ScriptManager para asignar un mensaje más amigable:
Figura 13.- El mensaje amigable en el error de lado cliente
No obstante, sería más adecuado que se mostrase de una forma más integrada dentro de la página, y no traducido a una excepción de JavaScript.
46 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
La parte cliente de ASP.NET AJAX ofrece soporte para interceptar de manera sencilla los distintos eventos en el ciclo de vida de las peticiones asíncronas. Vamos a sacarles partido para detectar desde JavaScript que se ha producido un error durante un evento de servidor asíncrono, y mostrar al usuario un mensaje dentro de la propia página, mediante capas. En la siguiente figura se puede observar el aspecto del mismo.
Figura 14.- Mensaje de error personalizado
Se muestra el mensaje de error dentro de una pequeña capa flotante, y se oscurece la página para darle más protagonismo al mensaje, e impedir al mismo tiempo que el usuario pueda pulsar sobre el resto de los elementos. Es una forma sencilla de simular un diálogo modal dentro de una página usando únicamente capas y estilos. El efecto de transparencia se consigue con un gráfico GIF semitransparente puesto como fondo de una capa. Para conseguirlo se debe incluir en la página un poco de código JavaScript que se encargue de interceptar un posible error al volver del postback asíncrono, y que luego se encargue de mostrar y ocultar la capa flotante sobre la que se mostrará el mensaje.
ASP.NET AJAX en el servidor 47
Nota: Esta parte es código de lado cliente, que forma parte de otro capítulo del libro, pero lo he incluido aquí porque está directamente relacionado con la gestión de errores. No vamos a ver los detalles del código JavaScript utilizado para conseguir toda la funcionalidad. Nos centraremos únicamente en la parte relacionada con la gestión del error, que es la que nos ocupa. Puedes ver el código completo del ejemplo en el ZIP con las demos del libro.
La clase PageRequestManager proporcionada en el lado cliente por ASP.NET AJAX sirve para acceder a toda la funcionalidad de postbacks asíncronos. Podemos obtener una referencia a la clase actualmente utilizada para realizar las peticiones con esta expresión JavaScript: Sys.WebForms.PageRequestManager.getInstance()
El método add_endRequest() de esta clase sirve para añadir nuevos manejadores al evento de finalización de llamada asíncrona. Es decir, que podemos ser notificados automáticamente de cuando un postback asíncrono regresa. Si definimos una función JavaScript con el nombre de, por ejemplo, EndRequesthandler, podemos usarla para interceptar el evento y así poder trabajar con los datos de la petición: Sys.WebForms.PageRequestManager.getInstance().add_ endRequest(EndRequestHandler);
La función recibe dos parámetros, al más puro estilo .NET aún siendo JavaScript, siendo el primero de ellos el “sender” y el segundo una referencia a un objeto de la clase EndRequestEventArgs. Esta clase sólo tiene dos propiedades interesantes: •
Error: contiene información del error. Como JavaScript no tiene propiedades, por convención se debe leer su contenido llamando al método get_error(). La clase Error devuelta tiene tres propiedades (message, number y name) que nos permiten averiguar respectivamente el mensaje, el número y el nombre del error.
•
errorHandled: se usa para indicar a la infraestructura de AJAX que el error ya lo hemos gestionado nosotros y que por lo tanto no debe dejar que se produzca. Se escribe en ella, por convención, llamando al método: set_errorHandled().
En el código de ejemplo descargable están todos los detalles comentados, pero basta indicar que lo que se hace es comprobar en un condicional si la propiedad Error contiene o no una referencia válida, en cuyo caso se muestra el mensaje de la figura anterior, y se indica que ya hemos gestionado nosotros el problema, para evitar que se manifieste como una excepción de JavaScript. Descárgate y échale un vistazo al código de ejemplo.
48 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
En aplicaciones que no usan AJAX lo habitual es que gestionemos los errores usando alguna página especial, que configuramos en nuestro web.config para que la infraestructura de ASP.NET se encargue de mostrarla. Por ejemplo, podríamos tener esta configuración:
Así, cuando se produzca un error en el servidor (código de estatus HTTP igual a 500) se mostrará automáticamente al usuario la página “ErrorServidor.aspx”. De similar manera se comportaría la aplicación ante intentos de acceso no autorizados (403) o páginas no encontradas (404), redirigiendo a las páginas correspondientes. Si se produce cualquier otra circunstancia no contemplada específicamente se muestra una página de error genérica “errorGenerico.aspx”. Por defecto el ScriptManager trata los errores producidos en el servidor para que sean totalmente compatibles con el comportamiento indicado en el web.config. Por lo tanto si hemos definido una página especial para errores de servidor en nuestra configuración y se produce una excepción durante un postback asíncrono, la infraestructura de ASP.NET AJAX se encarga de que todo funciones de la manera normal, redirigiendo a la página indicada. Debemos tener en cuenta pues, que en caso de haber configurado páginas de error personalizadas, no recibiremos jamás en el cliente notificaciones de los errores, y las técnicas mostradas anteriormente no funcionarán. La propiedad AllowCustomErrorsRedirect del ScriptManager sirve para ajustar este comportamiento. Por defecto vale True y obtenemos el funcionamiento habitual. Si la establecemos a False se hará caso omiso del web.config para los errores producidos durante repintados parciales. En el ejemplo descargable de la Web puedes quitar los comentarios en el web.config para habilitar una página personalizada para los errores 500. Verás cómo a partir de ese momento el diálogo de la figura 14 se deja de mostrar y aparece la página de error que se ha definido.
12.- Incompatibilidades de AJAX A la hora de usar el repintado parcial de los controles y las llamadas asíncronas debemos tener en cuenta algunas limitaciones. Son pocas pero pueden impactar negativamente en nuestros desarrollos si no las hemos considerado de antemano.
ASP.NET AJAX en el servidor 49
Algunos controles Web de ASP.NET, dado su modo de trabajar, no son compatibles con las actualizaciones parciales de la página y, por consiguiente, no están diseñados para funcionar dentro de un control UpdatePanel. La mayoría no ofrecen problemas, pero hay algunos que presentan incompatibilidades que, o bien impiden su funcionamiento o limitan alguna de sus características: •
Los controles de envío de archivos al servidor: FileUpload y HtmlInputFile, que sólo funcionarán de modo nativo si se realiza un postback síncrono común. En el mercado hay algunos controles de publicación de archivos que soportan asincronismo (por ejemplo el de Subgurim, gratuito y descargable desde http://fileuploadajax.subgurim.net), si bien no usan UpdatePanel para conseguirlo.
•
Los controles TreeView y Menu.
•
El control Substitution.
•
Los controles Login, PasswordRecovery, ChangePassword y CreateUserWizard cuyo contenido no ha sido convertido previamente en plantillas editables. Es una limitación con poco impacto porque, o bien se convierten a plantillas o bien se pueden utilizar las API de seguridad directamente desde JavaScript para conseguir el mismo efecto, como veremos en el próximo capítulo.
•
Los controles GridView y DetailsView cuando su propiedad EnableSortingAndPagingCallbacks es True. Por defecto no tienen activada esta propiedad por lo que no hay problema. Además, esta propiedad se utiliza precisamente para conseguir el mismo efecto que con el UpdatePanel (sólo que utiliza otras técnicas diferentes), por lo que no hay problema alguno en desactivarla dentro del panel. No se puede considerar una verdadera limitación.
Adicionalmente si utilizas Visual Studio 2005 con ASP.NET 2.0, en esta versión antigua del entorno de desarrollo y del Framework no están soportados en AJAX los controles relacionados con WebParts. Tampoco funcionarán los controles de validación, si bien se pueden hacer compatibles con sólo desconectar la validación de lado cliente (propiedad EnableClientScript = False). No existen estos problemas de compatibilidad con estos controles en versiones de Visual Studio superiores a la 2008, incluida ésta. Todos los demás controles funcionan dentro de los controles UpdatePanel. Sin embargo, en algunas circunstancias, un control podría no funcionar como era de esperar si lleva a cabo alguna de estas acciones: •
Registrar scripts en la página usando métodos de la clase ClientScriptManager. Esta clase se abandona en el caso de aplicaciones AJAX a favor de los méto-
50 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
dos equivalentes que ofrece el propio control ScriptManager, compatibles con las actualizaciones parciales. Si necesitas registrar scripts como parte de un postback usa éstos últimos. Lo veremos con detalle en el próximo capítulo. •
El uso directo de casi cualquier método de la clase HttpResponse no se llevará bien en general con las llamadas asíncronas. Por eso debemos evitar la llamada directa a los métodos: BinaryWrite, Clear, ClearContent, ClearHeaders, Close, End, Flush, TransmitFile, Write, WriteFile y WriteSubstitution. Especialmente habitual es la generación directa de scripts o etiquetas HTML durante el repintado de un control o durante el procesado de un evento mediante llamadas al método Response.Write, que es algo que debemos evitar.
13.- AJAX Control Toolkit Microsoft, conjuntamente con la comunidad de desarrolladores, ha creado el Kit de controles AJAX o AJAX Control Toolkit. Se trata de un conjunto de más de 30 controles que permiten extender y dotar de funcionalidad extra avanzada a controles comunes de ASP.NET. La funcionalidad de estos controles se centra en el lado del cliente, es decir, en mejorar las características de los controles normales cuando ya están renderizados en el navegador, pero se añaden y configuran desde el servidor, por eso los he incluido en este capítulo. Así, por ejemplo, gracias al Toolkit es muy sencillo hacer cosas como añadir un calendario desplegable a un TextBox para facilitar al usuario que escoja una fecha, o mostrar diálogos de confirmación integrados en la página, mejorar el aspecto de los mensajes de validación, crear animaciones, tener un editor HTML, etc. Por el mero hecho de utilizar estos controles obtendremos grandes mejoras en la usabilidad de las interfaces de nuestras aplicaciones Web ASP.NET. Los podemos usar en combinación con los repintados parciales o de manera completamente independiente, o lo que es lo mismo, requieren la existencia de un control ScriptManager en la página, pero no es necesario introducirlos dentro de un UpdatePanel ni utilizar éste ni ningún otro de los controles ASP.NET AJAX que hemos estudiado. Su uso es extremadamente sencillo. Lo más complicado sea tal vez instalar el Toolkit, así que vamos a explicar cómo obtenerlo y ponerlo en marcha en nuestro sistema. El AJAX Control Toolkit está disponible para descarga gratuita en CodePlex (el directorio de proyectos Open Source de Microsoft), y en concreto en la siguiente dirección: http://ajaxcontroltoolkit.codeplex.com/Release/.
ASP.NET AJAX en el servidor 51
Figura 15.- Página de descarga del AJAX Control Toolkit
La última versión allí disponible funcionará sin problemas tanto en Visual Studio 2008 como en Visual Studio 2010. Si estás usando Visual Studio 2005 con la versión 2.0 de la plataforma deberás buscar, un poco más abajo, las versiones anteriores específicas para este entorno. Como se observa en la figura 15 la página ofrece tres variantes para descargar. •
La primera versión “Binary” contiene únicamente las DLL necesarias para poder trabajar con el control desde Visual Studio. Es la variante que necesitamos si queremos distribuir el Toolkit con alguna de nuestras aplicaciones o para instalar en un servidor en el que se vaya a utilizar éste con una aplicación allí desplegada.
•
La variante “Source” contiene, además de los binarios compilados, todo el código fuente de los diferentes controles. Es una gran fuente de información y estudio si estamos interesados en cómo construir este tipo de controles. Es la más apropiada para ser utilizada en nuestro equipo de desarrollo.
52 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
•
Por fin la variedad “ScriptFilesOnly” contiene únicamente el código JavaScript que utilizan los controles para su funcionalidad en el navegador. Se trata de la funcionalidad de los controles en su estado puro, para usar directamente en el navegador. Esta es la variante que usarán los programadores más avanzados que emplearán directamente código JavaScript para instanciar los diferentes Behaviours de estos controles en sus páginas.
En la mayor parte de los casos lo mejor es bajarse la primera o segunda variedad. Una vez descargado el ZIP correspondiente, descomprímelo en una carpeta de tu disco duro en la que vaya a estar disponible todo el tiempo, ya que una vez añadidos los controles a Visual Studio los usaremos siempre desde allí. La variante “Binary” descomprimirá ya directamente la DLL y carpetas asociadas que necesitamos. En el caso de la descarga con código fuente, la DLL que usaremos está dentro de la carpeta “Binaries” una vez descomprimido el ZIP. Dentro de la carpeta de binarios del Toolkit hay una DLL, AjaxControlToolkit.dll, que contiene toda la funcionalidad de los controles, así como una serie de carpetas con identificadores de idiomas (como “es”, “ar”, “cs”, “de”...) que contienen ensamblados satélites con los textos adaptados a cada uno de estos idiomas. Ahora lanza Visual Studio y crea una nueva aplicación Web o abre cualquier aplicación Web que tengas ya hecha. Edita una página ASPX cualquiera para poder desplegar la barra de controles Web, como si fueras a añadir uno sobre la página. Al desplegar la barra de herramientas pulsa con el botón derecho sobre alguna zona libre de ésta y en el menú contextual añade una nueva sección. Llámala “AJAX Control Toolkit”. Vuelve a pulsar con el botón derecho dentro del área vacía de esta nueva sección y elige la opción “Elegir elementos” (“Choose Items” en la versión en inglés del entorno):
Figura 16.- Añadiendo controles a la nueva sección que hemos creado.
ASP.NET AJAX en el servidor 53
Al hacer esto aparecerá, tras una cierta espera, un diálogo como el de la figura 17, en el que podremos elegir qué controles .NET queremos que estén disponibles desde la barra de herramientas, y en concreto dentro de la sección actual.
Figura 17.- Diálogo de gestión de controles
Lo único que resta por hacer es, con el botón Browse de la figura, localizar la DLL del AJAX Control Toolkit (recuerda: AjaxControlToolkit.dll, en la carpeta correspondiente de la descarga anterior) y aceptar para que los controles se añadan automáticamente a la barra de herramientas. Nota importante: En Visual Studio 2010 este proceso fallará con un mensaje similar a este: “Could not load file or assembly ‘file:///C:\AjaxControlToolkit\AjaxControlToolkit.dll’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)” El motivo es el tratamiento especial que hace la versión 4.0 de .NET de los ensamblados bajados de Internet. El sistema operativo marca con un indicador especial a los ensamblados descargados de cualquier recurso remoto (Internet o una carpeta compartida en la red local). De este modo pueden ser reconocidos como posibles amenazas de seguridad al proceder, a priori, de un origen no confiable como es Internet. El sistema operativo los trata de una forma dife-
54 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
rente debido a esto aunque los tengamos copiados ya en una carpeta local. Sin embargo .NET en sus versiones anteriores a la 4.0 hacía caso omiso de esta información sobre el origen y al estar ubicados en local los trataba como ensamblados confiables. Como mejora de seguridad en .NET 4.0 se tiene en cuenta esta particularidad y limita la capacidad de todos estos .exe o .dll que se descarguen directamente o dentro de un ZIP desde una ubicación remota. Por este motivo la DLL del AJAX Control Toolkit no es capaz de cargarse en el entorno de Visual Studio. Para solucionarlo lo que tenemos que hacer es desbloquear su funcionalidad. Para ello localiza la DLL en tu disco duro y en sus propiedades verás un botón “Desbloquear” que sirve precisamente para esto (ver figura 18). Necesitarás permisos de administrador para que el botón funcione, así que si estás en una carpeta de sistema (como por ejemplo la de Archivos de Programa) mueve antes la DLL a otro lugar, cambia este atributo y devuélvela a su sitio original.
Figura 18.- Desbloqueo de ensamblado descargado desde sitio remoto. Ahora repite la operación anterior y verás como el problema desaparece y los controles se incorporan a tu barra de herramientas.
Una vez terminada la operación verás que en tu barra de herramienta aparecen multitud de controles nuevos, como se puede ver en la figura 19.
ASP.NET AJAX en el servidor 55
Figura 19.- Los controles del AJAX Control Toolkit
Aunque tengamos todos esos controles en la barra de herramientas, lo cierto es que, al trabajar con Visual Studio 2008 o Visual Studio 2010, no vamos a usarlos casi nunca desde ella. En Visual Studio 2005 sí que será necesario arrastrarlos desde ahí. En Visual Studio 2008 o superior las extensiones que ofrece el Toolkit están integradas con el entorno una vez las hayamos instalado. Así, al seleccionar un control en la página como un TextBox u otro cualquiera, éste presentará entre sus tareas asociadas una nueva acción llamada “Añadir extensor”:
Figura 20.- Acción de un control para extenderlo con el Toolkit
Al pulsar sobre la acción se abre un diálogo en el cual podemos elegir qué acción extensora, proporcionada por los diferentes controles del Toolkit, queremos añadir a nuestro control. Para cada control ofrece las acciones más apropiadas. Por
56 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
ejemplo en la figura 21 estamos añadiendo un calendario desplegable a un control TextBox, de forma que cuando los usuarios lo seleccionen se mostrará para ayudar a introducir fechas.
Figura 21.- Extensión de un TextBox con un calendario desplegable.
Esta extensor se corresponde con la funcionalidad del control CalendarExtender de la barra de herramientas (lo puedes identificar hacia la mitad de la columna de la derecha en la figura 19. A la hora de ejecutar la aplicación, todo el comportamiento que se observa en el navegador (en este caso la aparición del calendario y su funcionalidad) se proporciona directamente desde el lado cliente, mediante JavaScript. No hay postbacks al servidor para ofrecer estas características. La funcionalidad es compatible con la mayor parte de los navegadores modernos (Internet Explorer, Firefox, Safari...).
Figura 22.- El extensor para calendarios en funcionamiento básico
ASP.NET AJAX en el servidor 57
Por otro lado en la superficie de diseño en Visual Studio, a simple vista no ofrece ningún signo distintivo de que el control está usando esta extensión. Si desplegamos las acciones para el control, ahora además de tener la posibilidad de añadir extensiones (podemos añadir cuantas necesitemos), también está la opción para quitar alguna de las que se están usando. Otro signo visible de que el control está siendo extendido es que en sus propiedades ahora hay una nueva sección denominada “Extensores” que ofrece, agrupadas, las propiedades que controlan el comportamiento de cada uno de estas extensiones. En la figura siguiente se ven las propiedades para el calendario desplegable, con las que podemos amoldar su forma de funcionar a nuestras necesidades.
Figura 23.- Propiedades de controles extensores
En realidad la mayor parte de los controles del Toolkit son muy fáciles de utilizar y en esta obra no vamos a entrar en detalles sobre el funcionamiento particular de cada uno de ellos. En la dirección: http://www.asp.net/ajax/ajaxcontroltoolkit/samples/ se puede encontrar una aplicación de referencia en la que, uno a uno, se presenta cada control, su sintaxis y sus posibilidades. Si visitas la página podrás verlos en funcionamiento, y conocer los detalles de cómo sacarle partido.
58 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Figura 24.- Página de demostración y documentación del Control Toolkit
Esta misma aplicación de ejemplo se incluye dentro de la carpeta “SampleWebSite” de la variante “Source” de las descargas del Control Toolkit, así que puedes ejecutarla en local y mirar cómo está hecho cada ejemplo para aprender más todavía sobre ellos. En el código de ejemplo descargable desde la web del libro he incluido una pequeña página de ejemplo (“ControlToolkit.aspx”) en la que un cuadro de texto se extiende con un calendario y una marca de agua para indicar el formato a los usuarios. El botón de envío se extiende con un control ConfirmButtonExtender de manera que nos pregunte si queremos enviar los datos al pulsarlo, y los dos validadores incluidos se han extendido con sendos controles ValidatorCalloutExtender para dotarlos de mayor vistosidad. Ejecútalo para ver cómo, con sólo añadir estos extensores y sin código adicional, la interfaz se puede mejorar mucho.
Figura 25.- Un ejemplo de validador mejorado
Merece la pena dedicarle un tiempo para conocer bien el AJAX Control Toolkit, ya que mejorarán mucho la calidad de tus interfaces con un mínimo esfuerzo.
capítulo
4
ASP.NET AJAX en el navegador Con todo lo visto hasta ahora en el capítulo anterior, ya tenemos herramientas suficientes para desarrollar impactantes aplicaciones Web de alta velocidad de respuesta, sacando partido a los repintados parciales. Pero... ¿tendremos siempre realmente respuestas rápidas en la interfaz? ¿Es realmente todo tan fácil como parece? El ser humano no se caracteriza precisamente por la mesura en sus acciones. La propensión natural en cualquier ámbito es a abusar de aquellas cosas que nos gustan o nos reportan beneficio sin pararnos mucho a pensar en las consecuencias, sobre todo si son fáciles y parecen no tener efectos negativos. Por ello cuando los programadores Web con ASP.NET, acostumbrados a los postback, descubren las posibilidades que les otorga el binomio ScriptManager-UpdatePanel, lo habitual es que empiecen a usarlo en todas partes. Al fin y al cabo es muy fácil y “no hay que saber demasiado”: se arrastran un par de controles a una página normal y todo funciona de maravilla. Además, toda aplicación Web moderna que se precie debe ofrecer en la medida de lo posible este tipo de funcionalidad. El resultado habitual es que se colocan varios controles UpdatePanel en todas las páginas o, peor todavía, se mete un gran UpdatePanel en la página que contiene a todos los demás controles (créeme, lo he visto). Tras desarrollar toda la funcionalidad se prueba la aplicación y todo funciona de maravilla: rápido, sin molestos refrescos de la página y con un aspecto superprofesional. Los problemas vienen cuando la aplicación se pone en producción en el servidor. En las primeras pruebas se nota todo algo más lento pero no parece realmente preocupante. Al fin y al cabo estamos accediendo a través de Internet, no de la red local, y es normal que la velocidad sea algo menor. Pasan los días y las semanas. A medida que los usuarios de la aplicación aumentan, ya que tenemos más clientes o visitas, la cosa empieza a ir cada vez peor. Llega un punto en el que trabajar con la aplicación Web se vuelve insoportable de lo lenta que va. Miramos nuestro código para ver si tenemos alguna consulta a la base de datos que nos esté cargando el servidor y miles de posibilidades más, pero el problema de rendimiento no aparece. ¿Qué está pasando aquí? Lo que está pasando es que, a pesar de la aparente ligereza de las llamadas asíncronas con los Updatepanel, lo cierto es que (como ya se ha dicho) cada postback 59
60 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
asíncrono es un postback completo. Esto implica que se envía toda la información de todos los controles de la página al servidor, incluyendo el ViewState. En la página se recrea el árbol de controles, se procesan todos y cada uno de los eventos de éstos así como los de la página. Al terminar se devuelve el nuevo ViewState y el nuevo HTML para renderizar. La infraestructura de AJAX se queda sólo con el HTML correspondiente al UpdatePanel que se va a refrescar, pero en realidad se ha tenido que mover la página completa desde el cliente (el navegador) al servidor. Así que, como dicen los anglosajones: “There’s no such thing as a free lunch”, es decir, que nadie da algo a cambio de nada. Y en este caso la máxima es cierta también. No quiero decir que las técnicas que hemos visto hasta ahora sean dañinas para el rendimiento. De hecho se pueden aplicar en sitios más o menos grandes sin problema. No obstante no se deben utilizar de cualquier manera, y en muchos casos habrá que medir bien el uso que se hace de ellas. Su empleo indiscriminado sin entender realmente lo que está pasando por debajo, conduce a muchos problemas de rendimiento. Si tenemos claros los conceptos y buscamos la forma de hacer las cosas lo mejor posible en cada circunstancia nos ahorraremos muchas frustraciones y problemas en el futuro. Es por esto que Microsoft no concibió ASP.NET AJAX únicamente como un puñado de controles de servidor, sino que éstos se apoyan en una rica parte de programación en el lado cliente, de la que nosotros podremos aprovecharnos también para conseguir aplicaciones ágiles y optimizadas. Este capítulo profundiza en las principales técnicas de trabajo en el lado del navegador conducentes a mejorar mediante JavaScript las características de las páginas AJAX así como su rendimiento. Para seguir los próximos apartados es recomendable que tengas unos conocimientos fundamentales del lenguaje JavaScript así como de HTML y manipulación del DOM del navegador. Si no cumples estos requisitos puede que algún ejemplo concreto te cueste comprenderlo, pero si investigas un poco el código por tu cuenta no tendrás problema al final. Vamos allá...
1.- Retrollamadas de red a métodos estáticos Imagínate el siguiente ejemplo: tienes un formulario Web que contiene una rejilla llena de datos, varios controles de texto para entrada de información, algunos botones, listas desplegables para filtrado así como algunas etiquetas y controles de menor entidad. En una esquina hay una etiqueta que muestra un dato importante, un simple número (por ejemplo el stock en un almacén), y que el usuario de vez en cuando le gustaría ver actualizado. Dado que el resto de la página permanece inalterado, sólo por actualizar esa pequeña etiqueta no vamos a hacer un postback al servidor y recargar toda la enorme página ¿verdad?. Aparentemente se impone el uso de un útil UpdatePanel para con-
ASP.NET AJAX en el navegador 61
seguir un repintado parcial de la etiqueta. Así que allá vamos nosotros: lo colocamos rodeando a la etiqueta, establecemos el botón de refresco como disparador “et voilà”. Listo para poner en producción. Lo malo es que no nos hemos dado cuenta de que, cada vez que se refresca la pequeña etiqueta, en realidad estamos procesando la página completa en el servidor y transmitiendo a través de la Red enormes cantidades de información. Sólo el ViewState de la página puede ocupar cientos de Kb. El resultado: un rendimiento nefasto cuando lo ponemos al alcance de los usuarios.
1.1.- Análisis de tráfico con un UpdatePanel Vamos a ver el efecto con un ejemplo muy sencillo. Desarrollaremos el mismo ejemplo con una página común que utiliza el control UpdatePanel, y luego veremos lo mismo usando código JavaScript super-eficiente para comprender las diferencias. Ello nos dará una idea muy buena de cuándo merece la pena aplicar una u otra técnica. El proyecto completo está disponible en el ZIP con los ejemplos de código que puedes descargar de la web de Krasis Press. Lo encontrarás en la carpeta “AJAX_ ServiciosCliente”. Abre Visual Studio y crea una nueva aplicación Web. Elimina la página por defecto “Default.aspx” y añade una nueva con el nombre “HoraConUpdatePanel.aspx”. Arrastra sobre ella un ScriptManager, un UpdatePanel y, dentro de éste último, una etiqueta (roja y con letras grandes) y un botón. El aspecto de la página debería ser similar al de la figura 1.
Figura 1.- Aspecto de nuestra página de ejemplo
Como código para el evento de servidor que responde a la pulsación del botón vamos a escribir simplemente una línea que mostrará, dentro de la etiqueta, la hora actual en el servidor: Protected Sub cmdDameHora_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdDameHora.Click lblHora.Text = DateTime.Now.ToLongTimeString() End Sub
62 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Ya está todo lo necesario. Ahora lanza la aplicación y pulsa el botón varias veces. Verás que la hora se muestra actualizada en la etiqueta a toda velocidad y sin problema alguno. Vamos a ir más allá de la superficie y veamos qué está pasando por debajo. Para ello vamos a utilizar una de mis herramientas favoritas: Fiddler. Esta utilidad gratuita ha sido creada por Eric Lawrence de Microsoft (aunque sin soporte oficial por la compañía). Actúa como proxy en todas las peticiones Web que se hacen en el equipo y permite inspeccionarlas de forma que podamos conocer sus datos, la información devuelta en ellas, modificarlas al vuelo, y un sin fin de utilidades más. Se puede descargar desde www.fiddler2.com. Si ponemos en marcha Fiddler y pulsamos el botón que actualiza la hora en la etiqueta veremos que se produce una llamada al servidor. Si la inspeccionamos para ver sus contenidos obtenemos los datos que muestra la figura 2:
Figura 2.- Contenidos de la llamada al servidor con UpdatePanel
Aquí vemos que la llamada, dado que debe enviar el ViewState de los controles, lleva 268 bytes de información. Y eso en esta página tan sumamente simple. Ima-
ASP.NET AJAX en el navegador 63
gínate lo que enviará en páginas más complejas como la del ejemplo al principio de este epígrafe. Pero eso no es todo. La respuesta que contiene el nuevo Viewstate así como diversa información necesaria para actualizar el UpdatePanel ocupa 708 bytes. Es decir que en total, para actualizar una información -la hora- que ocupa únicamente 8 caracteres (o sea, 8 bytes) estamos generando un tráfico de 976 bytes, es decir, ¡122 veces mayor de lo necesario!. Además a esto hay que añadir la sobrecarga en el servidor, en el que se ha generado el árbol de controles y se han ejecutado todos eventos de la página y sus controles tan sólo para poder asignarle el valor a la etiqueta. Esto usa procesador y memoria y reduce la escalabilidad de la aplicación. Parece que al final lo de usar siempre un UpdatePanel no es tan buen negocio después de todo.
1.2.- Ejemplo optimizado con retrollamadas de red a métodos estáticos Está claro que en este ejemplo, a pesar de lo espectaculares que son los números relativos, el impacto es mínimo. Y en muchos otros casos puede que también sea así, y el uso de un UpdatePanel está más que justificado gracias a la productividad que obtenemos. No obstante vamos a ver cómo podemos crear una versión de esta misma página que esté optimizada y nos genere el mínimo tráfico posible. Así aprenderemos a solventar casos más graves en los que esta sobrecarga adicional no esté justificada o nos produzca problemas graves de rendimiento. Para ello vamos a explicar lo que se conoce como Retrollamadas de Red a Métodos Estáticos, o de manera más común, Métodos de Página. Esta técnica, como su propio nombre indica, nos permite definir dentro de la página uno o más métodos que serán accesibles directamente a través de Internet desde el navegador, siendo sólo necesario el uso de código JavaScript. De este modo en lugar de llamar a la página completa, que es lo que hacíamos antes, ahora llamaremos exclusivamente al método en cuestión, obteniendo una respuesta optimizada como enseguida veremos con el ejemplo. Añade otra página al proyecto con el nombre “MetodosDePagina.aspx”. Arrastra a su superficie de diseño un control ScriptManager. Vete a la vista de código fuente de la página e introduce una etiqueta
para definir una capa HTML. Otórgale el nombre “divHora” y en su estilo asígnale un color rojo y un tamaño de texto grande. Nos servirá para mostrar la hora y hará las veces de la etiqueta en nuestra versión optimizada del ejemplo anterior. Arrastra también, desde el grupo HTML, un botón HTML normal. En su atributo onclick asígnale el código JavaScript “MostrarHora();”, que enseguida definiremos.
64 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
El código final debería tener este aspecto:
Figura 3.- Código HTML de nuestro ejemplo optimizado
A excepción del ScriptManager no estamos utilizando controles de servidor, pero en cualquier caso daría igual porque, como veremos, ya no será necesario enviar el ViewState ni otros elementos de formulario que hubiese en la página ASPX. En el archivo Code Beside de la página (el .vb o .cs correspondiente) vamos a definir una sencilla función llamada DameHora() que devolverá una cadena con la hora actual en el servidor: _ Public Shared Function DameHora() As String Return DateTime.Now.ToLongTimeString() End Function
Al igual que si de un miembro de un servicio Web ASMX se tratara decoramos este método con un atributo WebMethod. Esto es indispensable para llamarlo desde el lado cliente. Otra cuestión importante a tener en cuenta es que el método que definamos para ser llamado desde el cliente debe ser un método estático (o Shared en Visual Basic). El motivo es que así no será necesario instanciar la página, lo que provocaría la misma carga que en el caso anterior al reproducir el árbol de controles, etc... El último detalle que nos queda es asignar a True la propiedad EnablePageMethods del ScriptManager, que por defecto contiene un False. Nota: Esta propiedad hay que establecerla sobre el verdadero ScriptManager que vayamos a utilizar. No se puede establecer en un ScriptManagerProxy por lo que si el nuestro está en un Master Page debemos marcarlo en ésta directamente y quedará habilitado para todas las páginas que la usen.
ASP.NET AJAX en el navegador 65
Cuando se cree el código HTML final resultante de ejecutar la página, la propiedad EnablePageMethods hará que se genere automáticamente en el navegador el código JavaScript necesario para llamar al método de la página. De hecho la llamada en JavaScript será tan directa y fácil como escribir: PageMethods.NombreMetodo
siendo NombreMetodo el nombre de alguno de los métodos de página que hayamos definido. A este método de JavaScript se le pasan dos argumentos: la función que se usará para recibir los resultados de la llamada, y la función a la que se llamará si se ha producido algún error. Con estas premisas, todo el código JavaScript que necesitaremos escribir en el navegador será el siguiente: <script language=”javascript” type=”text/javascript”>
Recuerda que la función MostrarHora es la que definimos para ser llamada cuando se pulse en el botón. La función $get() es una extensión global al lenguaje JavaScript de las muchas que define ASP.NET AJAX y que nos permite acelerar la escritura de código. En este caso $get es equivalente a llamar al método estándar del DOM, document. getElementById, es decir, devuelve una referencia al elemento HTML cuyo nombre le pasemos como parámetro. En el ejemplo nos devuelve una referencia a la capa divHora de la página. Con su propiedad innerHTML podemos introducir el HTML que necesitemos en su interior, en este caso la hora o el mensaje de error según lo que ocurra. Ejecuta la nueva página y pulsa el botón de actualizar la hora del servidor. Verás como de inmediato se muestra dentro de la capa en color rojo y funciona tan bien como la versión anterior. En apariencia no hemos ganado gran cosa puesto que todo parece ir igual de bien que en el ejemplo precedente. Como antes, vamos a analizar las peticiones al servidor
66 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
para ver qué información estamos intercambiando. Si capturamos con Fiddler la petición generada por el método de página veremos el resultado de la figura 4.
Figura 4.- Contenidos de la llamada al servidor con un método de página
¡Ahora sí que hemos optimizado el trabajo! La petición inicial ocupa 0 bytes, o sea va vacía porque no hay dato alguno que enviar. La respuesta nos devuelve en este caso el resultado de ejecutar el método en el servidor, codificado en formato JSON, listo para ser usado desde JavaScript. Su tamaño: 16 bytes. Y eso porque hay 8 bytes extra que se añaden a todas las respuestas debido a la definición del objeto JSON devuelto. Si el resultado fuera un dato más largo seguirían siendo sólo 8 bytes más y su importancia relativa sería menor aún. Es decir, hemos afinado al máximo el trasiego de información entre cliente y servidor, así como el código que ha de ejecutarse en el servidor, ya que no hay sobrecarga alguna por la ejecución de la página. Las ganancias de tamaño y rendimiento son mucho más espectaculares aún en páginas más grandes (reales) con más controles. Si vemos el código fuente de la página generada podremos analizar con detalle el código JavaScript autogenerado por el ScriptManager para conseguir toda esta funcionalidad. En la figura 5 se puede ver un fragmento del mismo.
ASP.NET AJAX en el navegador 67
Figura 5.- Código autogenerado para habilitar los métodos de página
Es evidente que para ciertos casos esta técnica ofrece grandes ventajas.
2.- Llamadas a servicios Web La técnica anterior está bien para trabajar dentro del ámbito de una misma página, pero presenta algunas limitaciones. La más importante de ellas es que nos dificulta la reutilización del código, ya que los métodos están atados a una página concreta. Si necesitamos utilizarlo en más de una página del sitio nos veremos obligados a definir otros tantos métodos estáticos de página. Otra limitación es que si usamos Master Pages en nuestro desarrollo y éstas son las que contienen el ScriptManager tendremos que habilitar la propiedad EnablePageMethods en éste, sea necesario o no para cada una de las páginas. Ello provocará que se genere código JavaScript para llamar a métodos de página que es innecesario en muchas de nuestras ASPX, ya que no los utilizarán. Una evolución mejorada de lo anterior es la capacidad del ScriptManager para habilitar las llamadas a servicios Web desde JavaScript. Ello nos permite tener en una única ubicación común todo el código reutilizable que vamos a llamar desde el navegador, eliminando la primera de las limitaciones anteriores. Además se puede habilitar selectivamente el acceso a estos servicios desde el control ScriptManagerProxy, por lo que sólo generaremos código JavaScript extra en las páginas que realmente lo necesiten.
68 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Nota: Por supuesto sólo podremos hacer llamadas a servicios Web que se encuentren ubicados en el mismo dominio que nuestras páginas. Esto no es una limitación de ASP.NET AJAX sino una característica de seguridad de todos los navegadores del mercado. Si no existieran esta y otras limitaciones similares (como la que restringe el acceso sólo a las cookies de nuestro mismo dominio), nuestra privacidad y seguridad como usuarios de Internet se verían seriamente amenazadas.
2.1.- Definición de un servicio Web ASMX para ser llamado desde JavaScript Vamos a aprender a utilizar los servicios web desde JavaScript mediante el desarrollo de un ejemplo práctico. Nota: No voy a explicar aquí los fundamentos de los servicios Web y su creación, ya que es algo que se sale del ámbito de este capítulo. Se supone que si estás leyendo este libro deberías saberlo ya. Si no es así te recomiendo mi libro de fundamentos de ASP.NET donde viene explicado. También podemos usar del mismo modo servicios creados con WCF (Windows Communication Foundation), algo que haremos en otros ejemplos.
Crearemos un pequeño proyecto que nos permita calcular la longitud de cualquier cadena con un método en un servicio Web, llamando a éste desde JavaScript. Obviamente hacer esto en el servidor es un sinsentido y es sencillísimo hacerlo directamente con JavaScript, pero lo utilizaremos únicamente para ilustrar los conceptos necesarios para consumir servicios Web desde el lado cliente. Agrega al proyecto un nuevo servicio Web llamado “Contador.asmx”. Elimina el método de ejemplo que trae y crea uno nuevo llamado ContarPalabras, cuyo objetivo será el de devolver el número de palabras contenidas en la cadena que se le pase como parámetro. El código es extremadamente sencillo pues sólo nos interesa centrarnos en cómo llamarlo desde JavaScript, pero podría ser todo lo complicado que quisiésemos, con llamadas a bases de datos o lo que fuera necesario. En la figura 6 se puede ver el resultado.
ASP.NET AJAX en el navegador 69
Figura 6.- El código de nuestro servicio Web de ejemplo
Tal y como está ahora mismo se trata de un servicio Web normal y corriente que acepta peticiones HTTP y devuelve sus resultados con XML. Por lo tanto sería factible realizar llamadas al mismo desde JavaScript usando el objeto XMLHttpRequest y procesando en el navegador el XML devuelto para operar con él. De hecho, como hemos visto en el capítulo de fundamentos, esta es la esencia original de AJAX (Asynchronous JavaScript And XML). Sin embargo también hemos visto que existen formas mejores de utilizar este tipo de recursos remotos. En concreto utilizando JSON (JavaScript Object Notation) para devolver los resultados. Y además sería fantástico si de alguna manera no tuviésemos que escribir a mano todo el código necesario para realizar la llamada y procesar los resultados ya que, como hemos estudiado, es algo engorroso y propenso a ciertos errores y situaciones problemáticas. Si te fijas en la parte de arriba del código en la figura anterior verás que está comentado un atributo de la clase que representa al servicio Web. Este atributo se llama ScriptService. Su propósito es habilitar la generación automática de código JavaScript especializado en el manejo del servicio con el que se decore. Este código autogenerado permitirá realizar las llamadas a los métodos del servicio directamente desde el navegador sin tener que preocuparnos de los detalles de bajo nivel. Esta “magia” se consigue gracias a un nuevo manejador para archivos .asmx que viene definido en ASP.NET AJAX y que sustituye al manejador habitual. Si abrimos
70 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
el web.config de nuestra aplicación y buscamos en el XML veremos que tenemos unos nodos como los siguientes:
Seguramente tendrá algunas entradas más, pero nos quedamos con esta que es la que nos interesa. Lo que hace esta sección de la configuración es eliminar el manejador de servicios Web por defecto (nodo ) y añadir uno nuevo especializado, ScriptHandlerFactory, que sabe interpretar este nuevo atributo que estamos estudiando y generar los formatos adecuados. No es necesario que toques nunca estos elementos. Simplemente quería llamar tu atención sobre ellos para que sepas cómo funciona esto por debajo. Para seguir con el ejemplo, descomenta el atributo ScriptService en el código del servicio para que pueda funcionar en modo JavaScript. Lanza la aplicación y navega hasta el servicio. Si escribimos el sufijo /js a continuación del nombre del .asmx, se generará automáticamente el código JavaScript necesario para llamarlo y podremos descargarlo a disco para su examen. La figura 7 muestra cómo hacerlo:
Figura 7.- Generación automática de un proxy JavaScript para el servicio.
ASP.NET AJAX en el navegador 71
Este será el JavaScript que usaremos de manera transparente en nuestra página para llamar al servicio cuando acabemos con el ejemplo. Es útil saber que podemos obtenerlo así, ya que nos permitirá examinarlo y aprender sobre su modo de trabajar. De hecho existe la opción de obtener una versión de depuración del mismo, mucho más apropiada para su análisis ya que contiene comentarios y el código está ordenado, si en lugar de /js utilizamos el sufijo /jsdebug. En la figura siguiente se puede ver un fragmento de los contenidos de este código que como vemos incluso lleva comentarios XML para generar documentación y obtener ayuda Intellisense desde el entorno de Visual Studio.
Figura 8.- Código JavaScript de la clase proxy para acceso al servicio
Este código hace uso de clases especializadas de la parte cliente de ASP.NET AJAX, como por ejemplo Sys.Net.WebServiceProxy, que encapsulan toda la funcionalidad necesaria. Su análisis va mucho más allá del ámbito de este libro pero te resultará fácil seguirlo si tienes buenos conocimientos de JavaScript.
2.2.- Creación de la página cliente para llamar al servicio Recapitulemos lo que hemos hecho hasta ahora, que es bien sencillo: hemos creado un servicio Web y lo hemos decorado con el atributo ScriptService para poder llamarlo desde JavaScript directamente. Ahora sólo nos resta crear una página de ejemplo que haga llamadas a nuestro flamante servicio y que ilustre la parte cliente del proyecto. Agrega una nueva página al proyecto, “UsarContador.aspx”. Arrastra a su superficie desde la barra de herramientas un ScriptManager y, desde el grupo de controles HTML, un control de texto, un de tipo botón y un
para contener el resultado de contar las palabras con el servicio web. Como vemos,
72 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
al igual que antes, no hemos utilizado ningún control de servidor a excepción del ScriptManager y todo el código que crearemos será de lado cliente.
Figura 9.- Nuestro ejemplo de uso del servicio Web
Lo único que tenemos que hacer para poder utilizar el servicio desde el JavaScript de nuestra página es registrarlo en el ScriptManager. Para ello utilizaremos una colección especial de este control llamada Services, y que hasta ahora no habíamos visto. Su misión es indicar al ScriptManager nuestra intención de utilizar uno o más servicios Web desde el navegador, consiguiendo que se genere automáticamente el código necesario para ello al renderizar la página. Si seleccionamos el control y vamos a sus propiedades veremos que hay un editor para esta colección, en el que podemos añadir referencias a servicios visualmente.
Figura 10.- El editor visual de referencias de Scripts
Los elementos introducidos en este editor se reflejan como entradas dentro de un nodo en el código HTML de la página. De hecho, los programadores
ASP.NET AJAX en el navegador 73
experimentados, dado que todo el código que van a escribir es de lado cliente, normalmente editan directamente este nodo en la vista HTML de la página, sin usar el diálogo visual anterior. Visual Studio nos ofrece soporte Intellisense para hacerlo (figura 11), por lo que es incluso más rápido.
Figura 11.- Establecimiento manual de referencias a servicios
Esta colección está disponible también en el control ScriptManagerProxy, por lo que podemos usarlas en entornos con Master Pages. A partir del momento en que hemos registrado el servicio, Visual Studio nos ofrece soporte Intellisense para su uso desde JavaScript (figura 12), obteniendo ayuda a medida que escribimos sobre sus métodos y propiedades (1) y sobre los diferentes parámetros de los mismos (2). Esto nos facilita sobremanera el trabajo con este sistema.
Figura 12.- Soporte Intellisense para el uso de los servicios con JavaScript
74 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
El código necesario para usar el servicio es directo y sencillo. Accedemos al mismo a partir del nombre de la clase y el nombre de sus métodos, exactamente igual a cómo haríamos desde código de servidor. Como parámetros se le pasaran los mismos que el método original en el servicio Web y, a mayores, una referencia a la función para procesar los resultados y otra a la función para gestionar los posibles errores. Exactamente igual que hemos visto en para los métodos de pagina. El último parámetro posible (userContext, como se ve en la figura 12) es opcional y sirve para pasar información específica de la llamada de forma que podamos distinguir unas de otras si hacemos varias llamadas seguidas al servicio, o si usamos los mismo manejadores de éxito y fracaso para varios servicios diferentes. Nuestro código final es tan sencillo como este: <script type=”text/javascript” language=”javascript”>
Como vemos prácticamente idéntico al que vimos cuando trabajábamos con métodos de página. Ahora ya podemos ejecutar la aplicación. Introduciremos una frase en el campo de texto y al pulsar el botón veremos como de inmediato y sin refrescar la página obtenemos el resultado correcto en el div habilitado a tal efecto.
Figura 13.- Nuestro ejemplo en funcionamiento
El usuario no notará en absoluto que se ha ido al servidor a realizar la tarea. El trasiego de información entre cliente y servidor es mínimo, ya que sólo se envía y se recibe información codificada como JSON (Figura 14). Además tenemos toda la funcionalidad concentrada en un solo punto y accesible para toda la aplica-
ASP.NET AJAX en el navegador 75
ción. Al tratarse de un servicio Web podremos sacarle partido a características de éstos como, por ejemplo, la caché de datos para mejorar el rendimiento.
Figura 14.- Análisis de la petición realizada al servicio web
El uso de servicios Web y servicios WCF diseñados para comunicación desde JavaScript en el cliente es realmente útil. Los podemos utilizar para intercambiar entre cliente y servidor todo tipo de datos complejos y no sólo simples cadenas y números, como hemos visto en los ejemplos. Desde el cliente además se pueden enviar datos al servidor (por ejemplo un registro de la base de datos para actualizar) y no sólo limitarnos a recibir información. De hecho las principales novedades de ASP.NET 4.0 en lo que respecta a AJAX tienen que ver con llevar cada vez más parte del procesamiento al lado del cliente y aprovechar la capacidad de llamar a servicios Web para generar las interfaces de datos puramente en JavaScript. En el siguiente capítulo profundizaremos en todo ello.
3.- Servicios de aplicación: Membership y Roles desde el navegador Uno de los servicios más útiles de ASP.NET es el de seguridad, encarnado en las clases Membership y Roles de System.Web.Security. Gracias a estas clases y el modelo de proveedores subyacente podemos gestionar de manera sencilla y flexible todas las operaciones de autenticación de nuestras aplicaciones Web.
76 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Con lo que hemos visto hasta ahora sería muy sencillo construir un servicio Web propio que nos permitiera hacer uso de todas las características de estas dos API directamente desde el navegador. De esta manera podríamos hacer autenticación de usuarios con JavaScript, verificar si el usuario actual pertenece o no a un rol y en función de eso tomar decisiones ya desde el cliente, etc... Lo cierto es que, sí, sería muy fácil, pero ni siquiera tendremos que hacerlo. ASP. NET AJAX ofrece “de serie” un completo juego de clases JavaScript especializadas que nos permiten utilizar las API de seguridad directamente desde el navegador. En realidad de lo que nos provee es de dos elementos que funcionan en cooperación. Por un lado, la primera pieza la tenemos en forma de dos nuevos recursos especiales en el servidor: •
/Authentication_JSON_AppService.axd
•
/Role_JSON_AppService.axd
Cada uno de ellos es un “endpoint” especial en el servidor al que nos podemos conectar para realizar llamadas con JSON a diferentes funciones que, por debajo, hacen uso de Membership y Roles. Ambos funcionan como un servicio Web normal y corriente basado en JSON, como el que nos podríamos haber construido nosotros mismos con lo visto hasta ahora. La otra pieza está, por supuesto, en el lado cliente. Se trata de las dos clases JavaScript complementarias de los dos servicios anteriores que hacen uso de éstos para proveer de funcionalidades de seguridad al navegador. Estas clases son: •
Sys.Services.AuthenticationService
•
Sys.Services.RoleService
Ambas clases disponen ya de los métodos apropiados para manejar los servicios Web correspondientes, por lo que no tenemos que conocer el funcionamiento de éstos para poder utilizarlos. De hecho los dos recursos .axd del servidor que he mencionado no están documentados, lo que es una forma de forzarnos a utilizarlos únicamente desde el JavaScript de ASP.NET AJAX. Nota: Si bien no están documentados ni tienen un WSDL para el servicio, resulta muy sencillo averiguar su funcionamiento usando alguna herramienta de intercepción de llamadas como Fiddler. Con ella podemos ver rápidamente qué métodos se llaman y con qué parámetros por lo que resultaría fácil sacar partido a estos servicios de seguridad desde aplicaciones escritas en otros lenguajes o usando framewoks de desarrollo JavaScript que no sean ASP.NET AJAX, como jQuery u otros. Una posible idea es integrar la autenticación de una aplicación antigua escrita en ASP 3.0 clásico con la seguridad de ASP.NET. Hay quien lo está utilizando para implementar la seguridad de sus aplicaciones Silverlight o PHP.
ASP.NET AJAX en el navegador 77
Estos servicios vienen desactivados por defecto por lo que, antes de pasar a la parte práctica de cómo usarlos, debemos explicar cómo los activamos para que estén disponibles desde el lado cliente. La forma de poner en marcha los servicios de Membership y Roles para lado cliente es desde el archivo de configuración de la aplicación Web, web.config, añadiendo los siguientes nodos: <scripting>
Por lo demás la configuración es la misma que haríamos para un uso normal de estas API, es decir, establecer qué proveedores queremos usar para la autenticación y los roles y crear los usuarios y roles iniciales pertinentes. Generalmente en una aplicación pública accesible desde Internet usaremos el proveedor por defecto, que maneja los datos de usuarios y los roles dentro de una base de datos SQL Server. En realidad funcionaría con cualquier proveedor que queramos, como por ejemplo el que valida los datos contra el Directorio Activo y que se entrega también con ASP.NET. Por supuesto podemos utilizar las API desde el cliente (con JavaScript y AJAX) y desde el servidor (llamadas normales a las clases) al mismo tiempo y de manera indistinta. Si autenticamos desde AJAX estaremos autenticados para código de servidor, y viceversa. Para ver el funcionamiento en la práctica de estas clases crearemos un ejemplo. El código completo puedes verlo en el archivo “Autenticacion.aspx” dentro de la carpeta “AJAX_ServiciosCliente” en el ZIP con las descargas del libro. En el ejemplo incluido existe una base de datos con tres usuarios (Admin, Tipo1 y Total) y tres roles (Administradores, Usuarios Tipo 1 y Usuarios Tipo 2), y en la página de ejemplo usaremos JavaScript para autenticar y cerrar la sesión de estos los usuarios, mostrando información sobre los mismos. Además hay una página protegida declarativamente con su correspondiente web.config dentro de la carpeta “Privado”, a la que sólo podremos acceder si antes nos hemos autenticado. Lo que hace el ejemplo es autenticar a los usuarios con AJAX y mostrar en la misma página información sobre a qué roles pertenecen. Cambia también la función del botón, de autenticar a cerrar sesión, según si el usuario está o no autenticado. En la figura 15 se puede ver el aspecto (más bien espartano) de esta página de ejemplo.
78 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Figura 15.- Usuario autenticado con AJAX mostrando información sobre sus roles.
Nota: Sería recomendable que descargases el código de ejemplo y lo tuvieses delante cuando leas lo que viene a continuación para ver in situ el código descrito y comprenderlo mejor. De otro modo te resultará mucho más difícil seguirlo.
En la página tenemos un par de controles de texto HTML para introducir el usuario y la clave de acceso, así como un botón que hará la llamada al servicio, para intentar autenticar al usuario o para cerrar la sesión si ya está autenticado. Cuando se pulsa este botón tiene lugar las siguientes instrucciones: function LogInLogOut() { if (Sys.Services.AuthenticationService.get_isLoggedIn()) Sys.Services.AuthenticationService.logout(null, HazLogout, HuboError, null); Else { Sys.Services.AuthenticationService. login($get(“txtLogin”).value, $get(“txtClave”).value, false, null, null, HazLogin, HuboError, null); } }
Lo primero que se hace es utilizar la propiedad isLoggedIn de la clase AuthenticationService para comprobar si hay algún usuario actualmente autenticado en el sistema. Devuelve un valor booleano para informar de ello. En caso de que lo haya, procedemos a cerrar su sesión con una llamada al método logout de la clase. Si no lo está, llamamos por el contrario al método login para efectuar la autenticación
ASP.NET AJAX en el navegador 79
validando las credenciales. En este caso, por simplificar, no hemos comprobado si los campos están vacíos o no antes de hacer la llamada. Para efectuar la autenticación y registro en el sistema del usuario el método login necesita varios parámetros aparte del nombre de usuario y la contraseña. En concreto la lista de parámetros que debemos pasarle es la siguiente: •
userName: el nombre de usuario a validar.
•
password: la clave correspondiente.
•
isPersistent: booleano para indicar si la cookie de seguridad generada será persistente o no. Es equivalente a marcar la casilla de “recordar al usuario” en el control Login. Hará que la validación perdure aún después de haber cerrado el navegador y mientras no se llame al método de cerrar la sesión o pase el tiempo de caducidad estipulado.
•
customInfo: este parámetro es de uso reservado y debe pasarse siempre un null.
•
redirectUrl: Dirección URL a la que redirige el navegador cuando la autenticación se realiza correctamente. Si le pasamos un null, no hay redirección y la devolución de la llamada se gestiona en la página actual, que es el caso que veremos en el ejemplo.
•
loginCompletedCallback: función a la que se llama automáticamente cuando el inicio de sesión termina. En ella determinaremos si la autenticación ha tenido éxito o no y realizaremos las acciones pertinentes en cada caso.
•
failedCallback: referencia a la función JavaScript que se llamará cuando la autenticación falle o se produzca un error.
•
userContext: datos de contexto de la llamada. Generalmente se le pasará un null.
Lo más interesante es ver cómo son las funciones que gestionan la devolución de la llamada. Dado que las llamadas AJAX a los servicios son asíncronas, el código JavaScript se sigue ejecutando y se notifica de manera automática su finalización llamando a la función de éxito o de fracaso según proceda. La función para gestionar los errores recibe como parámetros una referencia a la excepción, el contexto de la llamada (parámetro userContext, que normalmente será nulo) y el método que se ha llamado para llegar aquí (para poder reutilizar la función con varios métodos y así distinguir de cuál viene): function HuboError(excepcion, contexto, metodo) { MostrarInfo(“se ha producido un error: ” + excepcion. get_message()); }
MostrarInfo es una función auxiliar que he definido y que muestra por pantalla la cadena HTML que le indiquemos.
80 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
La función de resultado de autenticación tiene un delegado similar, pero en lugar de la excepción recibe como primer parámetro un valor booleano para indicarnos si la autenticación ha tenido éxito o no. Así, en nuestro caso el código es de la siguiente manera: function HazLogin(credencialesValidas, contexto, metodo) { if (credencialesValidas) { nomUsuario = $get(“txtLogin”).value;
Si en el primer parámetro se pasa un true, es que las credenciales eran válidas y el usuario está dentro del sistema. Es lo que controlamos en el condicional. En ese caso anotamos el nombre de usuario utilizado en una variable para poder guardarlo y provocamos la carga de la información sobre los roles a los que pertenece el usuario recién autenticado (enseguida vamos sobre eso). Nota:
desde mi punto de vista es una pena que el servicio no disponga de un par de métodos para obtener el nombre y el identificador único del usuario actual. Ello ayudaría mucho a relacionarlo con otras entidades de la aplicación directamente desde el cliente. No existe, por tanto, forma alguna de averiguar el nombre del usuario actual una vez este se ha autenticado. Si lo necesitamos deberemos habilitar por nuestra cuenta alguna forma de obtenerlo desde el servidor. No obstante como la autenticación se hace tanto en cliente como en servidor podemos usar todos los controles habituales —como el LoginName, LoginStatus, LoginView y similares— para mostrar esta información en otras páginas.
En el código anterior la única línea relevante es la que efectúa la carga de información sobre los roles de usuario. Hace una llamada al método load de la clase RoleService, el cual toma como parámetros la función a llamar al terminar la carga asíncrona de esta información (MostrarInfoUsuarioActual en este ejemplo), la que se llamará cuando haya un error (reutilizamos la que ya usamos para el método login), y un nulo para los datos de contexto. Este método load llama de manera asíncrona al servicio de manejo de roles en el servidor y al devolverse la llamada ejecuta la función que le indiquemos, en este caso MostrarInfoUsuarioActual. A esta función le pasa un array de cadenas de texto
ASP.NET AJAX en el navegador 81
que contiene la lista de roles a los que pertenece el usuario actual. En este caso nos limitamos a mostrarlos por pantalla recorriéndolos en un bucle: function MostrarInfoUsuarioActual(roles) { var s; s = “Usuario actual: “ + nomUsuario + “ ”; roles.sort();
}
for(var i = 0; i < roles.length; i++) { s += “Rol: “ + roles[i] + “ ”; } MostrarInfo(s);
Como ya he dicho, si el usuario está autenticado lo está tanto para el código de cliente como para el de servidor. Por ello la mayor parte de las verificaciones de seguridad se harán en el servidor. La clase AuthenticationService no ofrece método alguno para verificar la autorización de usuarios (por ejemplo para comprobar si tiene acceso a un determinado archivo), teniendo que hacerlo de la manera habitual en el servidor. La única función disponible para comprobar autorizaciones es la propiedad isUserInRole de la clase RoleService, a la que podemos llamar de la siguiente manera: Sys.Services.RoleService.get_isUserInRole(“Administrador”)
obteniendo verdadero o falso en función de la pertenencia del usuario actual al grupo que se le pase en el parámetro. En definitiva, los servicios automáticos de Autenticación y Roles de ASP.NET AJAX nos facilitan el acceso desde el propio navegador al trabajo más común de seguridad. Son más eficientes, en cuanto al uso de ancho de banda y carga del servidor, que utilizar dentro de un UpdatePanel los propios controles de seguridad que nos ofrece ASP.NET. No obstante tienen sus limitaciones y generalmente los usaremos sólo para hacer la autenticación y cierre de sesión, usando el resto de características desde el lado servidor. Esto no supone una gran limitación ya que el objeto de este tipo de funcionalidades de lado cliente es el de reducir el trasiego de datos entre cliente y servidor, y las funciones de seguridad y control de acceso, una vez hecha la autenticación, no suelen afectar en absoluto al tráfico intercambiado, así que suelen ser apropiadas.
4.- Referencias a Scripts en páginas y controles La principal función del ScriptManager es, tal y como se puede deducir de su nombre, la de registrar en el cliente diferentes bibliotecas de funciones JavaScript
82 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
que dotan de la funcionalidad necesaria para hacer uso de las características de ASP. NET AJAX. Tradicionalmente en ASP.NET ha existido para este propósito una clase especializada llamada ClientScriptManager que permite registrar scripts de manera única en una página, de forma que nos aseguremos de que no se registran dos veces y que se genera el código HTML apropiado. Esta clase dispone de los siguientes métodos: •
RegisterClientScriptBlock: permite enviar, desde código C# o VB de servidor, un bloque de JavaScript al cliente, otorgándole un nombre único, de forma que cuando se intente enviar más de una vez no se permita su duplicación. Genera un bloque de tipo <script>.
•
RegisterClientScriptInclude: registra en el cliente una referencia a un script contenido en un archivo externo, normalmente con extensión .js. Genera una etiqueta de tipo <script src=...”/>.
•
RegisterClientScriptResource: registra en el cliente una referencia a un script guardado como recurso en un ensamblado, de forma que no haya que distribuir el código en un archivo independiente. Genera una etiqueta de tipo <script src=”Webresource.axd?...” />.
El problema de esta clase tradicional accesible directamente desde las páginas, es que no funciona cuando participa en un repintado parcial de página. Si por ejemplo tenemos un control que, dependiendo de los datos que maneja, genera JavaScript usando los métodos anteriores en cada petición, al colocarlo en una página tradicional recibiremos nuevo JavaScript en cada postback. Como cada repintado de la página es completo y por lo tanto el HTML se vuelve a procesar entero, estos nuevos Scripts se interpretan en cada ocasión y todo funciona perfectamente. Si decidimos introducir un UpdatePanel para hacer repintados parciales de la página, de repente, todo ese código JavaScript dejará de funcionar. El motivo es que al repintar sólo una parte de la página, aunque llegase el nuevo JavaScript al navegador, éste no se interpreta y no tiene efecto alguno. El resultado de esto: muchos controles de terceros que funcionan perfectamente en páginas tradicionales dejan de hacerlo al usar ASP.NET AJAX. Para solucionarlo la clase ScriptManager ofrece exactamente los mismos tres métodos que acabamos de ver pero con la salvedad de que, por el mero hecho de usarlos desde ésta, ya funcionarán sin problema dentro de un UpdatePanel. Se trata de métodos estáticos de la clase, por lo que no tenemos que tener siquiera un ScriptManager en la página para poder utilizarlos. Por lo tanto, en lugar de utilizar este código para registrar uno de nuestros Scripts: Page.ClientScript.RegisterClientScriptInclude(“Utilidades”, “~/Scripts/Utils.js”)
En este caso se debe especificar una referencia a la página o control que registra el script así como a su tipo (los dos primeros parámetros). Para facilitar más aún el registro de scripts compatible con repintados parciales, el control ScriptManager ofrece una colección Scripts en la que podemos incluir las referencias de manera directa, sin escribir código en el evento Load de la página. Esta colección funciona de modo similar al de la colección Services que hemos visto en el apartado anterior, y tiene su propio diálogo visual (figura 16) así como un nodo () en el código HTML de la página. Por ejemplo:
En este caso estamos registrando una referencia a un script albergado en un archivo .js externo (primer ScriptReference), y otra a uno embebido como recurso en un ensamblado de forma que no tengamos que distribuirlo como archivo suelto.
Figura 16.- Diálogo de referencia de Script
Estas colecciones están disponibles también en el control ScriptManagerProxy, por lo que podemos usarlas en entornos con Master Pages.
84 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Nota: Hasta la versión 3.5 de la plataforma (incluida) la clase ScriptReference heredaba directamente de la clase Object. Al aparecer el Service Pack 1 de .NET 3.5 hubo un cambio interno en el código que hace que la clase ahora herede de System.Web.UI.ScriptReferenceBase. El efecto negativo de esto es que si en tu equipo de desarrollo tienes Visual Studio 2008 SP1 y despliegas la aplicación, pre-compilándola antes, a un servidor que tiene .NET 3.5 sin el Service Pack aplicado, obtendrás errores al intentar cargar las páginas con referencias a Scripts. En Visual Studio 2010 con.NET 4.0 aunque esto es así también no tendrás problemas porque ya deberás tener .NET 4.0 en el servidor para funcionar, aunque puedes compilar para la versión 3.5 de la plataforma, en cuyo caso lo anterior aplica también.
5.- Optimización de uso de bibliotecas en ASP.NET 4.0 Aunque una página esté vacía sólo por el hecho de colocar un, aparentemente inocuo, control ScriptManager en ésta, obtenemos en el lado cliente un buen puñado de código de Script, como se puede observar en la figura 17.
Figura 17.- Scripts generados por defecto al incluir un ScriptManager
ASP.NET AJAX en el navegador 85
La funcionalidad de cliente de ASP.NET AJAX está en realidad separada en diversos apartados, por ejemplo, extensiones globales de JavaScript, utilidades de formularios, operaciones de comunicaciones, globalización, seriación de objetos (JSON), etc... Si nuestra aplicación no requiere alguna de estas partes ¿por qué tenemos que descargarlas por completo en todas las ocasiones?. Lo ideal sería poder descargar selectivamente aquella funcionalidad que nos interese en cada caso. Hasta la llegada de ASP.NET 4.0 no había posibilidad de conseguir esto. Un ScriptManager en la página implicaba que teníamos toda la funcionalidad disponible en el cliente, la necesitásemos o no. A partir de .NET 4.0 ya no es necesario descargar los scripts completos aunque por defecto lo sigue haciendo para asegurar la compatibilidad con aplicaciones anteriores. El comportamiento se establece a través de la nueva propiedad MicrosoftAjaxMode, que puede tomar los siguientes valores: •
Enabled: se envían todos los scripts. Es el comportamiento por defecto para ser compatible con lo anterior.
•
Explicit: cada script que necesitemos se debe añadir explícitamente con una sección .
•
Disabled: todos los scripts propios de AJAX están deshabilitados.
Normalmente sólo usaremos el segundo valor para indicar manualmente qué partes de AJAX queremos usar. Así, por ejemplo, si sólo queremos usar las extensiones básicas para JavaScript que ASP.NET AJAX nos ofrece, podemos escribir esto:
Incluyendo exclusivamente el script correspondiente al núcleo de funcionalidad. Fíjate en que también he marcado EnablePartialRendering como False para evitar los scripts que generan la funcionalidad de los repintados parciales. Los módulos de funcionalidad que nos ofrece ASP.NET AJAX para el lado cliente son los siguientes: •
MicrosoftAjaxCore.js: extensiones base para el lenguaje JavaScript y el DOM.
•
MicrosoftAjaxComponentModel.js: soporte de controles JavaScript y behaviours.
•
MicrosoftAjaxSerialization.js: seriación y de-seriación de datos.
•
MicrosoftAjaxGlobalization.js: globalización y localización de scripts.
86 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
•
MicrosoftAjaxHistory.js: soporte para manipular el histórico de navegación (nuevo en ASP.NET 3.5 SP1).
•
MicrosoftAjaxNetwork.js: operaciones de comunicación a través de la Red.
•
MicrosoftAjaxWebForms.js: la funcionalidad para repintado parcial necesaria para que funcionen los UpdatePanels.
•
MicrosoftAjaxWebServices.js: soporte para manejo de servicios Web desde el navegador.
•
MicrosoftAjaxApplicationServices.js: servicios de autenticación, roles y perfiles.
•
MicrosoftAjaxTemplates.js: plantillas de datos de lado cliente (nuevo en ASP.NET 4.0).
•
MicrosoftAjaxAdoNet.js: soporte para ADO.NET Services (nuevo en ASP. NET 4.0).
Aun pudiendo elegir cuáles queremos incluir en la página no podemos hacerlo de cualquier manera, puesto que algunos dependen de otros para hacer su trabajo. Por tanto deberemos ser cuidadosos y tener en consideración las subordinaciones existentes entre ellos a la hora de decidir cuáles incluimos. En la figura 18 se muestran las distintas relaciones existentes entre los módulos.
Figura 18.- Relaciones de dependencia entre módulos de AJAX.
ASP.NET AJAX en el navegador 87
Nota:
Los módulos Templates y AdoNet tienen un tono ligeramente distinto para indicar que son soportados sólo en ASP.NET 4.0 (los demás son de ASP.NET 3.5). El módulo de Templates, que veremos en el próximo capítulo, depende del módulo WebServices únicamente cuando se quiere utilizar el control DataContext para enlazado a datos con JavaScript (aparecido en 4.0). Del mismo modo este módulo sólo dependerá de AdoNet si se usa el control AdoNetDataContext. El módulo WebForms se ha puesto en un tono diferente porque para incluirlo basta con establecer la propiedad EnablePartialRendering del ScriptManager como True, que es su valor por defecto.
Como ya he dicho, el orden de inclusión es importante, y debemos asegurarnos de que los módulos de los que dependen otros módulos se colocan en primer lugar. Cuanto más arriba o más a la izquierda en la figura anterior, antes deben ponerse en la lista de referencias. También existe la posibilidad de descargarse de www.codeplex.com/aspnet, en su apartado para AJAX, el código fuente de todos estos scripts e incluirlos directamente como referencias <script> de HTML normales.
5.1.- Combinación de scripts Otra cuestión importante a la hora de optimizar la descarga de Scripts es tener en cuenta el número de referencias a scripts que incluye la página, ya que ¿qué es más eficiente: descargar 100 Kb en un sólo archivo, o el mismo tamaño de información en 10 archivos diferentes? Obviamente lo primero porque nos estamos ahorrando 9 viajes de ida y vuelta al servidor. Como hemos visto en la figura 16, cuando utilizamos el ScriptManager así como otros controles basados en AJAX, como los del Control Toolkit, éstos generan una serie de referencias de tipo <script> en la página. Cada una de ellas supone una petición al servidor por cada referencia, por lo que al final la carga de la página se traduce en multitud de llamadas al servidor. Para tener en cuenta esta realidad y permitirnos optimizar la descarga de código JavaScript al navegador el ScriptManager, a partir de ASP.NET 3.5 SP1, ofrece una característica de combinación de scripts muy útil. Lo único que tenemos que hacer es rodear nuestro nodo en el ScriptManager con un nuevo nodo padre llamado , así por ejemplo:
88 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Al hacer esto todos los archivos incluidos en la colección de scripts se combinan de manera automática en uno solo, de modo que ello se traduce en una única llamada al servidor para descarga de Scripts. El tamaño sigue siendo el mismo pero al reducirse mucho el número de viajes hemos optimizado bastante el tiempo de respuesta de la página. En una página normal que use AJAX hay muchos más Scripts de los que pueda parecer a simple vista. En la página de Facebook de campusMVP (http://go.krasis. com/Facebook) podrás encontrar un vídeo práctico en el que el autor de este libro explica paso a paso cómo averiguar exactamente qué Scripts están siendo usados en una página, y cómo podemos posteriormente generar un script único como combinación de todos ellos para optimizar su descarga. El acceso directo al vídeo tiene esta URL: http://www.facebook.com/video/video.php?v=101426592913 Si usamos la capacidad de indicar manualmente qué módulos de Script queremos utilizar es que nos preocupa el rendimiento, así que deberíamos usar esta característica de combinación de Scripts también. Tenla en cuenta para tus páginas más importantes.
6.- En resumen En este capítulo hemos estudiado las cuestiones más importantes para el trabajo con AJAX en el lado del cliente. Al no ser este un libro de JavaScript, no hemos entrado en los detalles relacionados con la programación pura y dura en este lenguaje, que ASP.NET AJAX mejora mucho. Por el mismo motivo tampoco hemos visto algunas otras cuestiones relacionadas, como el uso de recursos JavaScript localizados o la creación de behaviours de cliente. El objetivo ha sido mostrar los fundamentos que nos van a ayudar a conseguir una visión más “pura” del desarrollo con AJAX, en el que el navegador cobra una gran importancia, responsabilizándose de la mayor parte de la generación de la interfaz. Esta visión es en la que se basan las características AJAX introducidas en ASP.NET 4.0 y que vamos a estudiar en el próximo capítulo.
capítulo
5
Enlazado a datos en el navegador El modelo tradicional de trabajo de las tecnologías Web (ASP, PHP, ASP.NET...) ha sido siempre el de generación de HTML en el servidor. Vamos a repasar el funcionamiento normal de una página ASPX que muestra datos enlazados desde algún origen (como una base de datos): 1. El usuario solicita la página al servidor. 2. Se obtienen los datos necesarios, se enlazan y se genera el HTML final de la página a partir de los diferentes controles Web que tengamos en la misma (renderizado del HTML). 3. El HTML se recibe en el navegador que se encarga de visualizarlo. Cuando el usuario efectúa cualquier acción que cambie el estado de la página (una ordenación, selección, borrado...), se envía el formulario al servidor (postback) y una vez allí se genera de nuevo todo el HTML para devolver al navegador. Esto es así independientemente de que la página utilice AJAX con controles UpdatePanel o no. El principal problema de este modelo (tampoco exento de ventajas) es la cantidad de datos que hay que mover constantemente entre el cliente y el servidor: el Viewstate de la página y el propio HTML generado. Ello provoca lentitud en la respuesta, carga en el servidor y multitud de “viajes” entre el cliente y servidor que muchas veces serían innecesarios. Un modelo alternativo sería el de traspasar más responsabilidades al navegador. En esta visión del desarrollo Web el servidor se encargaría de devolver únicamente dos cosas: las páginas HTML estáticas (es decir, no generadas dinámicamente) que solicite el usuario, y los datos que deban manejar éstas. El resto de la acción se desarrollaría en el navegador. Éste generaría dinámicamente la interfaz de usuario, gestionaría los eventos en local (en contraste con los eventos de servidor) y pediría o enviaría datos al servidor según la necesidad. Para entenderlo mejor comparemos una situación común como es la de mostrar los datos de una base de datos en una tabla. En el modelo tradicional, como hemos visto, obtendríamos los datos en el servidor y los enlazaríamos a un control de servidor (por ej. un GridView), el cual generaría la tabla y enviaría el HTML final al navegador. 89
90 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
En el modelo alternativo propuesto, la página prácticamente vacía llegaría al navegador. Éste solicitaría los datos a visualizar al servidor (mediante un servicio Web o similar) y se encargaría de generar la tabla correspondiente a partir de ellos, creando dinámicamente con JavaScript los elementos necesarios. Si se edita un registro de la tabla, en el primer caso se enviaría de nuevo la página al servidor, que actualizaría los datos y generaría de nuevo la página. En el segundo modelo el navegador enviaría únicamente los datos a actualizar al servidor, sin necesidad de hacer nada más porque el resto de la página permanece inalterada. Incluso podríamos acumular varios cambios en los datos y luego enviarlos todos juntos para su actualización en bloque, ahorrando viajes al servidor. La figura 1 muestra la comparación entre ambas concepciones del desarrollo Web.
Figura 1.- Modelo tradicional vs modelo “puro” de aplicaciones Web AJAX
Este modelo alternativo de AJAX sería más “puro”, en el sentido de que sólo se intercambian datos y no información de la interfaz. Acerca todavía más el mundo de las aplicaciones web al de las de escritorio, ya que en ambos casos la parte importante de la interacción con el usuario transcurre en el equipo de éste. Al usar esta técnica de trabajo es evidente que se obtienen muchas ventajas, sobre todo en lo que respecta al rendimiento del servidor, capacidad de respuesta de la interfaz y disminución radical de la cantidad de información que se trasiega por la Red. Las tecnologías que habilitan esta visión del desarrollo AJAX “puro” son dos: los servicios Web y las plantillas de lado cliente. En el capítulo anterior ya hemos podido comprobar lo sencillo que resulta emplear desde JavaScript los servicios Web habilitados a tal efecto con ASP.NET AJAX. Las plantillas de lado cliente son una
Enlazado a datos en el navegador 91
innovación aparecida con ASP.NET 4.0 que nos facilitan la gestión de interfaces complejas enlazadas a datos, directamente en el navegador. Nota: Este apartado es tal vez el más complicado de todo el libro, por lo que se recomienda seguirlo con calma y apoyándose en el código de ejemplo. Al trabajar con JavaScript en el navegador, conseguir que todo funcione correctamente a la primera suele costar un poco, pero los resultados son muy interesantes. No desesperes si al principio algunas cosas no te funcionan, es muy habitual en este caso.
1.- Concepto de plantillas de lado cliente Por regla general cuando tenemos que mostrarle al usuario datos homogéneos, utilizamos algún tipo de patrón de visualización que se repite de manera idéntica para cada elemento, particularizando sólo los valores de éstos. Así, por ejemplo, si tenemos que mostrar una lista de productos por pantalla podemos utilizar una tabla similar a esta:
......
Nombre
Precio
....
Producto 1
10 €
.....
Producto 2
20 €
.....
Tenemos la cabecera de la tabla con los títulos de las columnas y lo que hay en el cuerpo es una fila por cada uno de los productos que visualiza la información de cada uno de ellos. Del mismo modo podríamos haber elegido cualquier otra
92 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
distribución para los elementos: con listas desordenadas
o con elementos , etc... La cuestión aquí es que claramente hay un patrón que se repite a la hora de mostrar los datos. Este patrón que podemos identificar fácilmente es lo que va a constituir nuestra plantilla. En controles de servidor como el Repeater o el ListView trabajamos de una forma similar, pero el HTML se genera en el servidor para enviarlo al cliente. Imaginemos ahora que, tras haber identificado la parte común a los datos, pudiésemos escribir lo anterior de una forma parecida a la siguiente:
Nombre
Precio
....
{{ ProductName }}
{{ UnitPrice }}
.....
Fíjate que lo que hemos hecho es marcar una zona de nuestro HTML (lo que está dentro de la etiqueta ) como una zona genérica, indicando en ella los lugares donde irán los datos. Ahora será el propio navegador el que identifique esta zona y la utilice como un patrón a repetir para generar la tabla que necesitamos de manera automática a partir de los datos a visualizar, traídos desde el servidor. ¿No es realmente cómodo? Esta capacidad de autogenerar la interfaz a partir de plantillas nos brinda un gran control sobre el HTML generado y sobre todo nos desata del servidor a la hora de generar las interfaces de usuario. La definición de las plantillas en ASP.NET AJAX 4.0 es tan sencilla como lo que acabamos de ver, pues basta con identificar el elemento HTML que las contendrá, y marcar dentro del código interior los lugares en los que irán los campos. Hacer uso de ellas tiene algo más de dificultad, sobre todo porque disponen de capacidades avanzadas como el enlazado bidireccional, refresco automático, actualización de datos en el servidor, etc. Para aprender utilizarlas en la práctica vamos a crear un ejemplo y analizaremos el código poco a poco.
Enlazado a datos en el navegador 93
2.- Las bases para trabajar Para el ejemplo vamos a partir de la base de datos “Northwind” que hemos usado a lo largo del libro y vamos a crear una página que muestre información sobre los productos que vende esta empresa ficticia. Lo primero que haremos es crear un servicio Web que facilite información sobre los productos. Nuestro servicio, de momento, tendrá un único método llamado GetAllProducts, que devolverá una colección enumerable de objetos Product con la información de todos los productos en la base de datos. El servicio se llama “NorthwindService.svc” y lo tenemos dentro de la carpeta “Servicios”. Nota: En el ejemplo descargable desde la Web el servicio se ha implementado con Windows Communication Foundation, y la capa de datos con Linq2SQL. Tú puedes hacerlo como mejor te parezca, por ejemplo con ADO.NET “clásico” y un servicio Web ASMX de ASP.NET, como se ha visto en el capítulo anterior. En cualquier caso el servicio debe ser compatible con AJAX y debe devolver y aceptar objetos serializados en formato JSON. No nos pararemos en la implementación del servicio ya que lo que nos interesa es centrarnos en la funcionalidad AJAX. Consulta el código fuente en la descarga para ver los detalles.
Añadiremos una página al proyecto que contendrá únicamente un control ScriptManager. Como sabemos éste se encargará de generar todo el JavaScript necesario para sustentar la funcionalidad de ASP.NET AJAX. Hay que tener en cuenta una cosa, y es que el control no envía al cliente el código necesario para que funcionen las plantillas a menos que se lo indiquemos, para lo cual habrá que usar la referencia de script correspondiente:
Nota:
Es importante señalar que como toda la funcionalidad que vamos a crear es exclusivamente en el navegador, en realidad no nos hace falta siquiera este control. Lo usamos por comodidad en Visual Studio 2010. Basta con tener los scripts de ASP.NET AJAX en archivos .js externos (descargables desde http://www.codeplex.com/aspnet, en el apartado AJAX) y añadir referencias a los mismos en la página. Por ello todo lo explicado podría usarse con cualquier otra tecnología como PHP o incluso con páginas HTML puras y duras, sin código de servidor. Las referencias necesarias son las siguientes: <script type=”text/javascript” src=”Scripts/MicrosoftAjax.js”> <script type=”text/javascript” src=”Scripts/ MicrosoftAjaxTemplates.js”>
94 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
3.- Definición de la plantilla de productos Ahora que disponemos de la infraestructura necesaria (servicio + JavaScript) vamos a definir cómo queremos visualizar nuestra información. Ello requiere que escribamos el HTML de la plantilla a mano, en el código de la página. El editor de Visual Studio nos ayudará con la sintaxis. En este caso usaremos una tabla tal y como vimos hace un momento en el apartado 1. Debemos marcar el elemento contenedor de nuestra plantilla para que luego ASP.NET AJAX sepa qué partes del HTML se deben repetir. La forma de hacerlo es ponerle un identificador cualquiera, y asignarle una clase CSS especial llamada “sys-template”, así en nuestro caso:
Es importante que el nombre de la clase CSS sea exactamente ese, “sys-template”, ya que es la forma que tiene AJAX de identificar a este tipo de elementos. Debemos asegurarnos de que nuestra página, internamente o en un archivo .css externo, define la clase de esta forma: .sys-template { display:none; }
Con ello conseguimos que la plantilla no se vea en la página aunque falle la generación dinámica de la tabla. Si necesitamos utilizar otro estilo para el elemento (el estilo que normalmente tendría aplicado), podemos ponerlo también simplemente separándolo con un espacio, y se conservará tras el procesamiento:
Dentro de este elemento meteremos todo el HTML que queramos que se renderice para mostrar los datos, marcando la posición de éstos con una doble llave con el nombre del campo o propiedad del objeto que queremos visualizar. Así, en nuestro ejemplo que devolvemos información de un producto sacado de la base de datos, la plantilla podría quedar así:
{{ ProductName }}
{{ UnitPrice }}
Enlazado a datos en el navegador 95
4.- La clase DataView En estos momentos ya hemos decidido cómo vamos a visualizar la información, pero para que sirva de algo tenemos que indicar a AJAX cómo queremos utilizarla. La clase Sys.UI.DataView definida en la infraestructura de cliente de ASP.NET AJAX es la clave para activar las plantillas. Un DataView es un control de lado de cliente que nos permite relacionar un origen de datos y una plantilla para generar, de manera automática, el HTML necesario para visualizar la información. Los datos manejados por esta clase son muy variados: pueden ser matrices de objetos creadas en la propia página, datos resultantes de llamar a un servicio Web en el servidor o clases especializadas que ofrece AJAX para conectarse automáticamente a estos servicios Web (y que luego estudiaremos). Cuando se le asigna una matriz o colección de objetos, el DataView actúa como un repetidor de la plantilla, generando una instancia por cada elemento de la matriz. Se asemejaría a un control ListView o un Repeater de ASP.NET pero en el lado cliente. Si por el contrario se le asigna un único objeto la plantilla sólo se instancia una vez y el control actúa como un visualizador de entidades (se parecería a un control de servidor DetailsView). Existen tres maneras fundamentales de asociar datos a este control: 1. Asociarle directamente una matriz de objetos o un objeto en su propiedad data. Si, por ejemplo, tenemos una matriz de objetos ya definida en la propia página pues no se obtiene dinámicamente de un servicio externo. Es la forma menos habitual. 2. Indicarle la dirección relativa de un servicio Web que proporciona los datos, para lo cual la propiedad adecuada es dataProvider. Además en este caso es necesario indicarle mediante otra propiedad llamada fetchMethod el nombre de la operación del servicio que debe llamar para recuperar los datos. 3. Utilizar un objeto especializado para comunicarse con el servidor y usar éste como proveedor de datos para el DataView. Se asigna el objeto especializado directamente a la propiedad dataProvider. Estudiaremos estos objetos en un apartado posterior. Existen dos maneras de instanciar el control DataView y asignarle los datos y la plantilla: por código y de manera declarativa. Para instanciarlo mediante código lo más sencillo es utilizar el método especial de ASP.NET AJAX llamado $create. El mejor sitio suele ser el evento init de la clase Application, y que se ejecuta al cargar la página. La sintaxis sería la siguiente: Sys.Application.add_init(function() { $create( Sys.UI.DataView,
96 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
La creación toma como parámetros el tipo de clase a crear, las propiedades del objeto que queramos establecer durante la creación, los manejadores de eventos que queramos asignar (en este caso ninguno, por eso se pasa un nulo), las referencias a otros componentes que se necesiten (ninguna en el caso de este control), y finalmente la plantilla a la que queremos asociar el DataView. ¡Buff!. Parece complicado pero no lo es tanto una vez que te acostumbras. De todas maneras lo más habitual es utilizar el control DataView de manera declarativa, es decir, sin necesidad de escribir código. Esto es mucho más fácil y rápido, pero es importante conocer la técnica anterior (en código) por si fuera necesario en algunos casos complejos. Lo primero que necesitamos para poder usar el control declarativamente es indicar a la página nuestra intención, incluyendo el espacio de nombres apropiado dentro del cuerpo del HTML. Lo conseguimos con estos atributos:
Lo que estamos declarando son dos espacios de nombres que nos servirán para escribir atributos especiales en los elementos HTML sin que el navegador “se queje”, y que la infraestructura de AJAX sepa interpretarlos. Los nombres después de xmlns son los prefijos para los atributos. Así tendremos atributos sys y dataview. Puedes pensar en esto como en las directivas <@Register> de una página ASPX que luego nos permiten usar controles de usuario en ellas. El atributo sys:activate sirve para indicar en qué controles queremos utilizar los diferentes atributos (y por tanto controles) que estamos definiendo. Si ponemos un asterisco, como en el ejemplo, estamos indicando que los queremos usar con cualquier elemento de la página. La otra opción es utilizar una lista de identificadores de elementos separados por comas (por ejemplo: sys:activate=”productostemplate,txtNombre,txtApellidos”), que es más conveniente en páginas con mucho HTML ya que ofrece mejor rendimiento. Al definir los espacios de nombres apropiados ya podemos asociar controles DataView a plantillas con atributos HTML directamente, sin código. Por ejemplo nuestra tabla de productos quedaría así:
Enlazado a datos en el navegador 97
{{ ProductName }}
{{ UnitPrice }}
Fíjate que lo único que hay que hacer es adjuntar un control DataView a nuestro elemento contenedor de la plantilla gracias al atributo sys:attach. El resto son asignaciones de propiedades para que el nuevo control pueda funcionar, las cuales se establecen con el prefijo dataview definido antes, seguido del nombre de la propiedad y el valor adecuado para cada una. Así, en este ejemplo lo que estamos haciendo con las propiedades es: •
Indicar que el proveedor de los datos es el servicio Web ubicado en “Servicios/NorthwindService.svc”, relativa a la página actual.
•
Que el método que debe llamar para obtener los datos es “GetAllProducts”.
•
Que la llamada debe hacerla usando el método GET, y no el método POST que se usa por defecto. El motivo es que el servicio Web que hemos creado es de tipo REST (http://es.wikipedia.org/wiki/REST) y así lo requiere.
•
Finalmente con la última propiedad le estamos diciendo que debe recoger automáticamente los datos y visualizarlos nada más crearse el control, para que no tengamos que forzarlo mediante código. Así se obtendrán y enlazarán los datos nada más cargarse la página.
El orden en el que se asignen es indiferente. Hacer esto es equivalente al código de inicialización que vimos antes, por lo que nos lo ahorramos y podemos hacer el enlazado de manera más sencilla y directamente en el HTML. Recapitulemos un momento todo lo que hemos necesitado: 1. Definimos el HTML de la plantilla indicando donde van sus campos y marcando con la clase sys-template al elemento contenedor de la misma. 2. Declaramos en el cuerpo que queremos usar declarativamente el espacio de nombres Sys y el control Sys.UI.DataView, activando de paso los elementos que nos interesen. 3. Activamos la plantilla asignando a su contenedor un control DataView y estableciendo las propiedades necesarias.
98 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Como vemos es más sencillo y directo de lo que parece con todas las explicaciones intermedias, necesarias por otra parte. Con esto ya tendríamos la funcionalidad deseada. Si ahora ejecutamos la página veremos que se visualizan los datos de los productos de manera asíncrona tras cargar la página. Lo único que hemos transferido desde el servidor son la página original (prácticamente vacía) y los datos que necesitábamos mostrar. Todo lo demás se ha hecho en el cliente con JavaScript.
Figura 2.- La tabla resultante generada en el cliente
De momento no es muy espectacular, pero en los siguientes apartados iremos mejorando paulatinamente el ejemplo a medida que aprendemos más características del enlazado de datos en el cliente.
5.- Pseudo-columnas y atributos especiales Vamos a mejorar un poco la visualización de nuestra tabla de datos con varias características adicionales. En primer lugar vamos a hacer que las columnas pares tengan un color diferente a las columnas impares. Con este objetivo vamos aprenderemos a usar las pseudo-columnas de datos. Estas pseudo-columnas son campos especiales que se pueden utilizar en las plantillas y que nos ofrecen información de contexto sobre los datos que estamos enlazando. En concreto en la versión actual disponemos de las siguientes pseudocolumnas: •
$index: nos indica el número (basado en cero) del registro que el DataView está procesando actualmente.
Enlazado a datos en el navegador 99
•
$dataItem: se trata de una referencia al objeto de datos que se está usando en este momento para generar la plantilla, por lo que podremos usarlo para obtener información sobre el mismo con reflexión, llamar a alguno de sus métodos o lo que pudiésemos necesitar.
•
$id(“identificador”): nos permite generar un identificador único para cada elemento de una plantilla, generado a partir de la cadena que se le pase.
•
$element: permite el acceso al elemento de la plantilla que se está procesando.
Así, el primer añadido que vamos a hacer a nuestra tabla de datos es colocar una columna extra al principio de la misma que nos muestre el número de cada registro. Tras añadir la cabecera
correspondiente podemos incluir este código en la plantilla:
{{ $index + 1 }}
Como $index está basado en cero le sumamos 1 para que tener una numeración más apropiada para el usuario. Otra característica que necesitaremos son unos atributos especiales para plantillas llamados atributos class. Antes de poder usarlos debemos declararlos en el cuerpo de modo análogo a como lo hicimos antes con los prefijos sys y dataview. La etiqueta del cuerpo quedaría así:
A partir de este momento tenemos la capacidad de usar atributos class para establecer clases CSS condicionadas en los elementos de las plantillas. Lo que se consigue es asignar una clase CSS al elemento sólo en el caso de que se cumpla una determinada condición. En el ejemplo que nos ocupa vamos a usar una clase CSS para las columnas pares y otra diferente para las columnas impares, por lo que combinándolo con la pseudo-columna $index que acabamos de ver, la plantilla quedaría así:
{{ $index + 1 }}
{{ ProductName }}
{{ UnitPrice }}
100 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Fíjate que ahora la fila de la tabla tiene un par de atributos class. El primero asigna la clase CSS de nombre “impares” a aquellas filas cuyo índice no sea divisible entre dos (es código JavaScript normal y corriente que usa el operador % para calcular el módulo de una división), y asigna la clase “pares” en caso contrario. De esta forma conseguimos un aspecto diferente en filas alternas de la tabla.
6.- Atributos sys condicionales Del mismo modo que podemos asignar clases de manera condicional, existen unos atributos especiales del espacio de nombres sys que trabajan de manera parecida. En este caso lo que nos permiten es establecer algunos atributos comunes de los elementos HTML. Se trata de los siguientes: •
sys:checked: se usa para generar el atributo checked de los elementos HTML que lo soportan, indicando una condición para ello. Lo usaremos ahora mismo en nuestro ejemplo.
•
sys:disabled: idem que el anterior pero para el atributo disabled que permite desactivar controles.
•
sys:id: se usa para generar identificadores de elementos mediante código JavaScript. Por ejemplo, para generar un identificador a partir del identificador de producto podríamos escribir: sys:id=”{{ ‘Prod_’ + ProductID }}”.
•
sys:src: para generar atributos SRC a partir de código. Muy útil por ejemplo para colocar rutas a imágenes sacadas de una base de datos.
Sabiendo esto vamos a añadir una columna más a nuestra plantilla para que se visualice el estado de disponibilidad de cada producto. Hay un campo en la base de datos llamado Discontinued, que es verdadero si el producto está descatalogado. Podemos aprovecharlo para mostrar esta información usando un checkbox de HTML, mediante un atributo sys condicional del siguiente modo:
Es decir, que sólo aparecerá marcado si el valor del campo Discontinued es true. Con estos cambios nuestra nueva plantilla mostrará la lista de productos de la manera que se observa en la figura 3.
Enlazado a datos en el navegador 101
Figura 3.- tabla resultante de nuestra plantilla mejorada.
7.- Atributos code para renderizado condicional Una cuestión habitual al generar HTML en el cliente es que necesitemos que algunos elementos se creen sólo al cumplirse ciertas condiciones. Por ejemplo, al enlazar datos a una lista desplegable que actuará de filtro para otra tabla, si nos encontramos en el primer elemento que también se renderice un elemento adicional vacío que sirva para deshacer el filtro. Este ejemplo en concreto lo tienes en el código del ZIP, en la página “Default.aspx”. Las variantes de este atributo son tres: •
code:if: si el resultado del código de este atributo es verdadero se renderizará el elemento, obviándolo de la interfaz en caso contrario.
•
code:before: ejecutará, justo antes de procesar el elemento para la plantilla, el código JavaScript que le asignemos como valor. Útil para hacer cosas durante el procesamiento que puedan afectar al resto de la plantilla.
•
code:after: idem que el anterior, pero ejecutando el código al terminar de procesar el elemento actual.
Estos atributos son muy útiles cuando los combinamos con las pseudo-columnas que estudiamos anteriormente.
102 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Para poder utilizarlos hay que declarar su espacio de nombres en la etiqueta body, con lo que nos quedaría de la siguiente forma (ahora es el tercer espacio de nombres declarado):
Vamos a aprovechar estos atributos para añadir a nuestra tabla de datos una indicación visual sobre el stock actual de los productos. Vamos a indicar tres niveles diferentes: verde, naranja y rojo, según si tenemos mucho stock, poco o ninguno en absoluto. El valor del inventario lo conocemos gracias al campo de la base de datos llamado UnitsInStock. Añadiremos una nueva columna a la tabla que contendrá la definición de los tres niveles de stock con sendas imágenes:
= 20 “ /> 0” />
Hemos incluido en cada una de ellas un atributo code:if con una condición que define cuándo debe generarse el elemento en la plantilla. De este modo si el stock actual del producto es superior a 20 unidades, consideramos que tenemos mucho y se mostrará una marca de verificación de color verde. Si es mejor de 20, indicaremos un stock mermado con una admiración naranja. Finalmente si no hay stock se indicará mediante un aspa roja.
Figura 4.- Nuestra tabla con indicadores de nivel de stock.
Enlazado a datos en el navegador 103
Estas expresiones nos dan mucho juego para determinar qué elementos queremos mostrar en cada momento y también para actuar sobre variables globales o sobre elementos que se van a procesar a continuación o que acaban de renderizarse.
8.- Enlazado de datos en tiempo real Hasta ahora hemos enlazado objetos usando la sintaxis de la doble llave {{ }}, conocida como evaluador de expresiones en línea. Como deducimos de su nombre, su objetivo es evaluar las expresiones JavaScript que hayamos escrito dentro. Una vez hecho esto no hay ningún tipo de relación posterior con los elementos subyacentes. De hecho podemos utilizarlas para evaluar cualquier tipo de expresión JavaScript, no es necesario que sea una propiedad del objeto que estamos enlazando. El uso de esta sintaxis nos otorga un gran poder, pero ofrece una limitación fundamental: el enlace se realiza sólo una vez, al procesar la plantilla. Una vez visualizada la información, si se produce cualquier cambio en los datos subyacentes, éste no se verá reflejado en la interfaz. La funcionalidad de plantillas de ASP.NET AJAX permite mantener sincronizados, en tiempo real, los datos enlazados y la interfaz de usuario. De esta manera si la información varía (porque la hemos editado en otro lugar de la página, por ejemplo), sus nuevos valores se ven reflejados de inmediato en todas las plantillas que los usen. Esta funcionalidad se basa en el uso del patrón de diseño Observer, gran conocido por los programadores aficionados a la arquitectura de software, y en especial los que vienen del mundo Java. Nota: No vamos a entrar en detalle aquí sobre el funcionamiento de este patrón, pero baste decir que es suficiente utilizar una llamada al método makeObservable de la clase Sys.Observer (incluido por las extensiones de JavaScript de ASP.NET AJAX), para que automáticamente nuestro objeto genere eventos ante todos los cambios que se produzcan en sus propiedades. Y esto es independiente de la funcionalidad de plantillas por lo que podemos sacarle partido en nuestros propios desarrollos para otras cosas.
El enlazado a datos en las plantillas tiene una sintaxis alternativa que habilita el refresco en tiempo real de la interfaz. En lugar de usar una doble llave, se emplea una llave simple junto con la palabra clave binding. Es decir, en lugar de escribir: {{ ProductName }}
escribiremos {binding ProductName }
y automáticamente, a partir de ese momento, si cambia el valor del nombre de producto, este cambio se verá reflejado automáticamente en la interfaz generada a partir de la plantilla. Y todo esto sin tener que escribir ni una línea de código. Esta característica se denomina Live Binding o Enlazado en Tiempo Real.
104 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Dependiendo del elemento HTML al que enlacemos los datos, el enlace puede ser en un solo sentido (single-way) o bidireccional (two-way). Por ejemplo si enlazamos un campo a un control input de texto, lo que escribamos en éste hará que se modifique el valor subyacente, modificando los datos originales. Sería un enlace bidireccional, puesto que los cambios en el objeto se reflejan en el control y viceversa. Sin embargo si lo enlazamos a un elemento de sólo lectura (por ejemplo a un ), el enlace sólo será posible en un sentido, ya que el elemento al no cambiar no puede afectar a los datos originales. Todo esto nos resultará de gran ayuda para enviar información modificada al servidor como veremos enseguida. Mientras tanto vamos a ver un ejemplo rápido de uso que, si bien no será muy útil, nos dará una idea precisa de cómo funciona el Live Binding. Para ello enlazaremos una plantilla a un elemento de la página, y no a un origen de datos. Así también veremos cómo se puede usar para muchas más cosas que para enlazarnos a datos traídos desde el servidor. Nota: Este ejemplo lo tienes en las descargas del libro, dentro de la carpeta correspondiente a las plantillas de datos (VS2010_PlantillasDatosCliente_AJAX), en el archivo llamado “BindingEnDosSentidos_EjBasico.aspx”.
Arrastremos un control de texto HTML () a la página. A continuación, dentro de un que actuará de contenedor de nuestra plantilla, definiremos otro y un nuevo control de texto. La sintaxis final será esta:
/>
{binding value}
En este caso usamos el dataView para enlazarnos directamente contra el primer cuadro de texto. Aquí no hay datos traídos del servidor ni código alguno, sólo sintaxis declarativa. Fíjate que no hay tampoco código de servidor, y que podríamos haber usado una página con extensión .htm siempre que incluyamos los Scripts de ASP.NET AJAX como archivos externos y no con un ScriptManager. En la parte correspondiente a la plantilla hemos usado Live Binding para mostrar el valor del campo de texto (propiedad value de éste, por eso se escribe
Enlazado a datos en el navegador 105
{ binding value }) en una etiqueta () y en otro cuadro de texto. ¿Qué ocurrirá al ejecutarlo? Abre la página y juega con ella un poco. Verás que todo lo que escribas en el primer cuadro de texto se verá automáticamente reflejado en los otros dos elementos en cuanto quites el foco de él. Al mismo tiempo, como el segundo cuadro de texto tiene un enlace bidireccional con el primero, al escribir algo en él y quitarle el foco, el cambio aparecerá de inmediato en el primero y en la etiqueta. En la práctica lo que hemos conseguido es mantener sincronizados ambos cuadros de texto y la etiqueta. Fíjate además que la propia infraestructura de AJAX se ocupa de evitar que entremos en un bucle de cambios infinitos cuando uno cambia al otro. Esta funcionalidad nos otorga un enorme poder para el manejo de datos en el cliente, y en los próximos apartados vamos a ver cómo sacarle todo el partido posible.
9.- Vistas maestro-detalle: preparar el maestro Vamos a seguir mejorando nuestro ejemplo para, al mismo tiempo, aprender nuevas características de las plantillas de lado cliente. En esta ocasión vamos a añadirle la posibilidad de mostrar un detalle de los registros al pulsar sobre ellos. Se trata de la típica vista maestro-detalle que hay en casi todas las aplicaciones: facturas-líneas de factura, proveedor-artículos, etc... En esta ocasión vamos a mostrar en otro lugar de la página, con una nueva plantilla, los datos seleccionados en nuestra lista de productos. Lo primero que vamos a ver es de qué manera podemos provocar la selección. Existen tres atributos especiales del espacio de nombres Sys que están especializados en generar acciones para los elementos de las plantillas. Se trata de: •
sys:command: el nombre del comando que queremos lanzar. Actualmente el único comando estándar existente es “Select” para seleccionar elementos. Podemos, no obstante, emplear cualquier otra cadena arbitraria para definir comandos propios, y así generar eventos adaptados a nuestras necesidades.
•
sys:commandargument: argumentos que queremos pasarle al comando, para particularizarlo. Quizá un identificador, una referencia a un elemento, etc...
•
sys:commandtarget: el elemento sobre el que queremos actuar. Normalmente no se utiliza.
En nuestro ejemplo vamos a hacer que se seleccione el producto actual en el momento en el que el usuario pulse sobre cualquier punto de fila de la tabla de productos. Redefinimos la fila (elemento
en nuestro ejemplo) añadiéndole el atributo sys:command, así
/>
106 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Esto provoca que el procesador de plantillas incluya el JavaScript necesario para conseguir ese efecto. El evento de selección es automáticamente reconocido por el control dataView. Si ahora probamos el ejemplo, lo cierto es que no notaremos diferencia alguna con lo que ya teníamos. Necesitamos algo más para que se manifieste visiblemente la selección. El control dataView está diseñado para trabajar en equipo con sys:command, y dispone de una propiedad llamada selectedItemClass. Ésta sirve para asignar automáticamente un estilo CSS al elemento que provoca el comando. Define en la página del ejemplo una nueva clase CSS, de nombre “.seleccionado”, que establezca el color de fondo en un tono que destaque sobre los restantes utilizados en la página. Ahora añade este atributo al elemento contenedor de la plantilla: dataview:selecteditemclass=”seleccionado”
Ejecuta de nuevo la aplicación. Ahora sí que seleccionarás de manera visible cada fila, ya que al hacer clic sobre una de ellas ésta adoptará el estilo de nombre “seleccionado”. Los datos que queremos visualizar como vista de detalle de nuestros productos son los campos del producto actualmente seleccionado. Los dataView tienen una propiedad llamada selectedData que devuelve precisamente esta información. Necesitamos alguna manera de identificar al dataView de los productos para poder acceder a sus propiedades y así poder leer los datos del elemento seleccionado. Aquí es donde entra el último atributo que veremos dentro del espacio de nombres de sistema: sys:key. Con él podemos asignar de manera declarativa identificadores JavaScript a los componentes, como el dataView, que activemos en los elementos HTML. Para otorgar un nombre a nuestro dataView de productos añadiremos el siguiente atributo al elemento contenedor de la plantilla de productos (míralo en el código de ejemplo): dataview:sys-key=”TablaProductos”
De este modo a partir de ahora podremos acceder a este dataView desde otros elementos de plantillas utilizando el identificador que le hemos asignado, en este caso “TablaProductos”. Fíjate en que como se está aplicando un sys:key como atributo secundario de dataView, se usa un guión y no dos puntos en la declaración anterior: dataview:sys-key.
10.- Vistas maestro-detalle: preparar los detalles Recapitulemos lo que hemos hecho hasta ahora: •
Hemos usado sys:command para indicar qué elemento hará la selección.
Enlazado a datos en el navegador 107
•
Establecemos el estilo de los elementos seleccionados con la propiedad selectedItemClass del dataView.
•
Empleamos sys:key para darle un nombre accesible al dataView de productos que actuará de maestro en esta visualización maestro-detalle.
De acuerdo. Ahora que ya sabemos seleccionar visualmente un elemento de las plantillas, veamos cómo podemos enlazarlo con otra plantilla que visualice los contenidos correspondientes al detalle. En este caso para visualizar los detalles usaremos un conjunto de etiquetas y controles de texto que además nos permitirán editar los campos de datos. Para simplificar el ejemplo sólo mostraremos los campos correspondientes al nombre del producto y a su precio. El código necesario para obtener esta vista es el que se muestra en la figura 5.