Está en la página 1de 8

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

53

Python DESARROLLO

La nueva tecnologa web.

AJAX

AJAX es la palabra de moda, Google usa AJAX, Yahoo usa AJAX todo el mundo quiere usar AJAX pero lo usas t? y ms importante an qu demonios es AJAX? POR JOS MARA RUIZ Y PEDRO ORANTES
Brave New World (Un Mundo Feliz) es el nombre de la famosa novela de Aldous Huxley, en ella nos muestra un mundo distinto y aterrador pero que pareca, y parece, cada vez ms cercano. Nosotros no tenemos una visin tan pesimista del mundo, pero es probable que ese ttulo (que se podra traducir literalmente por un nuevo y desafiante mundo) explique todo el revuelo que est levantando AJAX. El trmino fue acuado por Jesse James Garrett en el artculo [1] de la tabla Referencias. Durante mucho tiempo las GUIs, las Interfaces Grficas de Usuario, han dominado la informtica. La gente que trabajaba en la Web siempre estaba intentando convencer a todo el mundo de que para la mayora de los programas, un interfaz web bastaba. Pero los usuarios estaban acostumbrados a ciertas caractersticas, como el auto-completado

de campos o el arrastrar y soltar, que eran imposibles en la Web. Conforme avanzaba el tiempo numerosas empresas y personas proponan soluciones. La lista es interminable: JavaScript, Java Applets, ActiveX, Tcl, VBScript, Macromedia Flash Pero todas fallaban de uno u otra manera. En el caso de Java, para ejecutar el Applet necesitabas tener instalado el Java Runtime Environment, y la mayora de los usuarios no saban ni qu era aquello que se le peda. Lo mismo ocurra con Macromedia Flash. Lo peor era que cuando estaba solucionado el tema de la instalacin del software adecuado, los desarrolladores creaban, y crean, pginas horribles llenas de cosas movindose que distraen e irritan. Se sentan impulsados a usar hasta la ltima capacidad de las nuevas herramientas y acababan generando monstruosidades.

Esta fase ya casi ha pasado y ahora se busca la sencillez, y en el momento justo surgi AJAX. Para ms informacin ver url [2] de la tabla Referencias.

Los problemas con IE


Internet Explorer, a pesar de ser el primero que introdujo XMLHTTPRequest, es el que ms problemas da en su uso. El cdigo aqu mostrado ni siquiera funciona en IE debido a que en l se hace uso de un componente ActiveX para establecer la conexin. Existen numerosas tcnicas para permitir la compatibilidad entre navegadores, pero debido a la extensin del artculo y a su complejidad no las hemos mostrado. El lector interesado en la compatibilidad puede estudiar el cdigo de sistemas de cdigo libre que implementan AJAX como puede ser Sarissa. Vea la referencia [3] en la tabla Recursos.

WWW.LINUX- MAGAZINE.ES

Nmero 12

53

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

54

DESARROLLO Python

Pero qu es AJAX?
Muy buena pregunta. Lo cierto es que AJAX ha estado delante de nuestras narices todo el tiempo, esperando a que alguna mente despierta lo redescubriese. El acrnimo AJAX se compone de las palabras Asynchronous JavaScript and XML, trmino acuado por Jesse James Garrett, y curiosamente su existencia se debe a una de esas famosas violaciones de los estndares que suele realizar Microsoft con sus productos. All por 1998, Microsoft introdujo dentro de sus productos una librera que le permita hacer consultas usando el protocolo HTTP de manera autnoma y asncrona. Cuando tu navegador accede a una pgina y esta contiene cdigo Javascript, este cdigo a su vez puede traer informacin de esa u otras pginas de manera independiente. Si adems se hace que este cdigo permanezca en ejecucin respondiendo a eventos tenemos entre manos la

posibilidad de traer informacin al navegador sin recargar la pgina. Esto es til para algunas tareas pero no demasiado, ya que a nuestro puzzle le faltan piezas. La primera pieza es la adopcin de esta librera por casi todos los navegadores, por lo tanto el cdigo pasa a ser de aplicacin universal. Adems resulta que podemos modificar el contenido de la pgina en tiempo real usando el denominado rbol DOM. Y por si fuese poco, cuando AJAX fue definido, los programadores comenzaron a usar protocolos XML para comunicarse con los servidores. Qu quiere decir esto? Pues que ahora, con AJAX, podemos cargar una pgina y, sin tener que recargarla, traernos informacin, modificar la pgina en tiempo real, e interactuar con servidores remotos usando protocolos XML. Bsicamente, una vez cargada la pgina web tenemos entre manos todas las posibilidades de programacin de un GUI tradicional. Y todo esto sin necesidad de plugins ni instalaciones, toda esta tecnologa est en nuestros navegadores esperando ser usada.

ser consultado usando AJAX. Crearemos una web con algo de cdigo Javascript que a intervalos acceder a nuestro servidor Python y modificar el aspecto de la pgina web.

Los 5 Ingredientes
Los cinco ingredientes necesarios para elaborar nuestro producto son CSS, Javascript, HTML, XML y Python, como aparecen en la figura 1, y cada uno tiene su funcin en esta obra. HTML es la base sobre la que vamos a trabajar, definimos una pgina web en la que todo ocurrir. De hecho, con el paso del tiempo el propio HTML ha acabado convirtindose en una especie de plantilla donde campan a sus anchas CSS y Javascript. CSS nos permite otorgar propiedades visuales a los elementos de HTML. Javascript es el encargado de actuar en la mquina cliente, en el navegador, y puede modificar tanto el HTML como las propiedades visuales que CSS define. Con la llamada XMLHttpResponse sus atribuciones se han disparado. Ahora se ve como un lenguaje de programacin de pleno derecho. En los prximos aos puede que adquiera mucha ms importancia de la que ha tenido hasta ahora.

Cmo encaja Python?


Pues vamos a realizar un pequeo servidor de contenidos en Python que pueda

Listado 1: fichero server.py


01 02 03 04 05 06 07 #!/usr/local/bin/python import BaseHTTPServer import os import cgi class AJAXHTTPRequestHandler (BaseHTTPServer.BaseHTTPReques tHandler): """ Responde a peticiones HTTP """ def do_GET(self): "Gestiona los GET" 19 20 21 "/uname.xml": ["envia_comando","uname -a"]} envia_fichero(self,ruta,fichero): 33 # No usamos ruta, pero as simplificamos el cdigo 34 p = Pagina(fichero) 35 self.enviar_respuesta(p.tipo(), p.contenido()) 36 37 def envia_comando(self,ruta,comando): 38 c = Comando(comando) 39 self.enviar_respuesta(c.tipo(), c.contenido()) 40 41 def enviar_respuesta(self, tipo, contenido): 42 self.enviar_cabecera(tipo) 43 self.wfile.write(contenido) 44 45 def enviar_cabecera(self, tipo): 46 self.send_respon-

08 09 10 11 12 13 14 15

acciones = { "/" : ["envia_fichero","index.html"] , 16 "/ps.xml" : ["envia_comando", "ps afx"], 17 "/df.xml": ["envia_comando", "df"], 18 "/who.xml": ["envia_comando","who"],

if self.path in acciones.keys(): 22 accion = acciones[self.path] 23 (getattr(self,accion[0]))(self .path,accion[1]) 24 else: 25 if (self.path[-3:] == ".js" or 26 self.path[-4:] == ".css"): 27 self.envia_fichero("",self.pat h[1:]) 28 29 else: 30 self.envia_fichero("","404.htm l") 31 32 def

54

Nmero 12

WWW.LINUX- MAGAZINE.ES

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

55

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

56

DESARROLLO Python

XML es el nuevo lenguaje estndar de intercambio de informacin. Prcticamente cualquier lenguaje dispone ya de libreras para generar y analizar documentos XML. Dentro del mundillo AJAX se ha convertido en el estndar para el intercambio de informacin con el servidor y para la serializacin de objetos con protocolos como JSON. Y, como no, Python. En nuestro caso se va a encargar tanto de realizar las tareas de servidor HTTP como de recolectar informacin importante y confeccionar con ella ficheros XML. Tenemos que compenetrar todos estos elementos para realizar nuestro proyecto. El objetivo es que los componentes HTML, Javascript, CSS y XML sean tan estticos como sea posible, debido a que todos ellos interactan con el usuario. Es en el servidor Python donde debemos aportar la flexibilidad necesaria como para aadir nuevas caractersticas sin tener que cambiar ninguno de los otros elementos.

Figura 1: Esquema de nuestra aplicacin AJAX con todos sus compenentes.

La parte de Python: BaseHTTPRequest


Python dispone en sus libreras estndar de muchos esqueletos para distintos tipos de servidores. Entre ellos encontra-

mos BaseHttpRequest. Esta clase nos permite construir servidores HTTP sin excesivo esfuerzo, as que la emplearemos. Pero no debemos usarla directamente, sino a travs de la clase BaseHttpRequestHandler, que es la encargada de gestionar los eventos que se suceden en el servidor. Por tanto heredaremos de ella y crearemos una clase llamada AJAXHttpRequestHandler, ver Listado 1 (todos los listados de este artculo pueden descargarse de [4]). Un servidor HTTP recibe distintos tipos de comandos, pero el que nos inte-

resa es el comando GET. Es el usado para solicitar informacin al servidor. Cada vez que se realice una peticin GET se invocar el mtodo do_GET de BaseHTTPRequestHandler as que lo vamos a redefinir en nuestra clase. Cuando se invoque do_GET, en la variable de instancia self.path se encuentra la ruta solicitada por el cliente. Nosotros contrastaremos esta ruta contra las que aceptamos. Si no se encuentra entre ellas, devolveremos la clebre pgina 404, indicando que la pgina solicitada no existe. La informacin se devuelve

Listado 1: fichero server.py (cont.)


se(200) 47 self.send_header("Content-type ","text/" + tipo) 48 self.end_headers() 49 50 class Pagina: 51 def __init__(self,nombre): 52 self.nombre = nombre 53 self.texto = "" 54 fichero = file(self.nombre) 55 self.texto = fichero.read() 56 fichero.close() 57 58 def contenido(self): 59 return self.texto 60 61 def tipo(self): 62 tipo = "html" 63 ext = self.nombre[-4:] 64 if (ext == "html"): 65 tipo = "html" 66 elif (ext == ".xml"): 67 tipo = "xml" 68 elif (ext == ".css"): 69 tipo = "css" 70 return tipo 71 72 class Comando: 73 def __init__(self,comando): 74 self.tuberia = os.popen(comando) 75 self.xml = "" 76 77 def contenido(self): 78 # fichero XML 79 if not self.xml: 80 self.xml = "<?xml version=\"1.0\" ?>" 81 self.xml += "<salida>" 82 linea = self.tuberia.readline()[:-1] # para quitar el \n 83 while linea: 84 self.xml += "<linea>" + cgi.escape(linea)

+ "</linea>" 85 linea = self.tuberia.readline()[:-1] 86 self.xml += "</salida>" 87 return self.xml 88 89 def tipo(self): 90 return "xml" 91 92 def test(HandlerClass = AJAXHTTPRequestHandler, 93 ServerClass = BaseHTTPServer.HTTPServer): 94 BaseHTTPServer.test(HandlerCla ss, ServerClass) 95 96 if __name__ == '__main__': 97 test()

56

Nmero 12

WWW.LINUX- MAGAZINE.ES

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

57

Python DESARROLLO

usando el mtodo self.wfile.write(), lo que en l escribamos, ser devuelto al cliente que realiz la peticin. Adems del fichero index.html, ofreceremos una serie de servicios en forma de ficheros XML. Estos servicios consistirn en la ejecucin de un comando de sistema, y la conversin de su salida en un fichero XML. El formato del fichero ser muy sencillo:

Figura 2: La clase BaseHTTPRequest genera una entrada por comando.

desde envia_respuesta antes de enviar el fichero en s. Es de esta manera como el navegador determina el tipo de fichero que recibe y sus caractersticas. Cuando arranquemos el servidor, veremos que van apareciendo mensajes correspondientes a los distintos comandos que se le mandan. La clase BaseHTTPRequest genera una entrada por cada una. Ver figura 2.

Listado 2: fichero index.html


01 <html> 02 <head> 03 <title>Pruebas con AJAX</title> 04 <link rel="stylesheet" href="estilo.css" type="text/css" /> 05 <script language="Javascript" 06 src="ajax.js"> 07 </script> 08 </head> 09 <body> 10 <div id="documento"> 11 <h3 id="titulo">Informacin del sistema</h3> 12 <input type="button" name="button" value="Procesos" 13 onclick="javascript:hazPeticio n('ps.xml');" /> 14 <input type="button" name="button" value="Disco" 15 onclick="javascript:hazPeticio n('df.xml');" /> 16 <input type="button" name="button" value="Usuarios" 17 onclick="javascript:hazPeticio n('who.xml');" /> 18 <input type="button" name="button" value="Mquina" 19 onclick="javascript:hazPeticio n('uname.xml');" /> 20 <div id="contenedor"> 21 <div id="datos"></div> 22 </div> 23 </div> 24 </body> 25 </html>

<?xml version="1.0"?> <salida> <linea>linea de salida</linea> ... <linea>linea de salida</linea> ... <linea>linea de salida</linea> </salida>

Gestores de servicios
Para gestionar los servicios se han creado las clases Pagina y Comando, que responden a los mismos mtodos. La primera, Pagina, se encarga de las peticiones de ficheros de texto, que en nuestro programa se reducen al fichero index.html y sus ficheros supletorios, fichero Javascript y CSS. Bsicamente los carga en una variable y, mediante el mtodo contenido, las dems clases tienen accesos a los mismos. En este caso, el parmetro ruta no afecta a esta clase, pero se ha definido para que guarde compatibilidad con la clase Comando. La clase Comando ejecuta el comando especificado y permite el acceso al texto devuelto por l mismo mediante el mismo mtodo que la clase Pagina. De esta manera son intercambiables. Ambas clases poseen un mtodo tipo() que devuelve el tipo de fichero que almacenan. En el caso de Comando, siempre ser un fichero XML, pero Pagina debe adivinar el tipo de los ficheros que

Por lo que generaremos el fichero XML a mano, sin hacer uso de libreras. De esta manera, cuando el cliente solicite el fichero ps.xml, nuestro servidor ejecutar el comando ps afx, crear el fichero ps.xml con la salida del comando y se lo enviar al cliente.

Definicin de servicios
Para permitir que la definicin de servicios sea lo ms simple posible, basta con introducir una nueva entrada en el diccionario acciones de la clase AJAXHTTPRequestHandler con la siguiente estructura:
<ruta> : [<mtodo_a_invocar>,U <comando_a_ejecutar>],

Cuando se gestiona el comando HTTP GET, se busca en este diccionario la ruta. En caso de que est presente, se ejecutar el mtodo almacenado usando como parmetros la ruta y el comando. Esto nos da gran flexibilidad, aadir un nuevo servicio consiste en introducir una nueva linea de cdigo. Existe un detalle importante, todo fichero devuelto usando HTTP debe tener una cabecera con una serie de lneas con formato llave: valor que dan informacin al cliente, el navegador, sobre el fichero devuelto. Debido a problemas de espacio hemos decidido devolver slo el formato del fichero. Para ello se invoca el mtodo envia_cabecera

WWW.LINUX- MAGAZINE.ES

Nmero 12

57

053-060_PythonL12

16.11.2005

9:01

Uhr

Pgina

58

DESARROLLO Python

devuelve. Para ello se comprueba la extensin de los mismos. En aras de la simplicidad, slo consideraremos tres extensiones. Cuando un comando es ejecutado por Comando, se tiene especial cuidado en utilizar la funcin cgi.escape sobre cada linea. Esta funcin realiza algunas conversiones dentro del texto que se le pasa para que pueda ser correctamente visualizado por un navegador web. El problema radica en que ciertos caracteres, como pueden ser < or son especiales y si no se escapan, si no se preceden de un \, causarn problemas. Y con esto, hemos definido un servidor web bsico. Python nos permite realizar complejas tareas con poco cdigo y

este es uno de esos casos. Vayamos ahora a por AJAX para comprenderlo.

Javascript, desde otra perspectiva


Mucha gente ha tenido extraos encuentros con este lenguaje de programacin. Es raro y, hasta hace no demasiado, no muy til. Te permita modificar colores en pginas web o poner insidiosos banners. Por no hablar de las famosas ventanas emergentes. Esto ha hecho que se haya ganado una fama muy mala, tal es as que casi todos tenemos restricciones en nuestro navegador en torno a qu acciones puede o no realizar Javascript. Lo ms normal es que tengamos uno de esos famosos bloqueadores de popups. Pero Javascript se ha reinsertado en la sociedad de los programadores por la puerta grande gracias a un solo objeto.

HTML
Quiz ste sea uno de los artculos donde Python tenga menor protagonismo, pero con cinco actores suele ser complicado. Veamos el HTML. La pgina index.html se puede ver en el Listado 2. Bsicamente carga un fichero Javascript, un fichero CSS y muestra un ttulo junto a unos cuantos botones. Estos botones invocan acciones en Javascript. Debemos fijarnos especialmente en el uso del atributo id en numerosas etiquetas HTML. Gracias a estos ids podremos manipularlas mediante Javascript.

Listado 3: fichero ajax.js.


01 // GLOBALES 02 var http_request = false; 03 04 function hazPeticion(url) { 05 http_request = false; 06 http_request= new XMLHttpRequest(); 07 if (http_request.overrideMimeType ) { 08 http_request.overrideMimeType( 'text/xml'); 09 } 10 11 if (!http_request) { 12 alert('Error al crear la instancia de XMLHttpRequest.'); 13 return false; 14 } 15 16 // Esto es un callback, que se dispara al terminar de 17 // descargar el fichero xml. 18 http_request.onreadystatechange = modificaContenido; 19 http_request.open('GET', url, true); 20 http_request.send(null); 21 } 22 // Elimina todo elemento con id "linea" 23 function vaciaContenido(){ 24 var d = document.getElementById("contenedor"); 25 while(document.getElementById( "linea0") || 26 document.getElementById("linea 1")){ nodo = document.getElementById("linea 0"); if (! nodo){ nodo = document.getElementById("linea 1"); } var nodo_basura = d.removeChild(nodo); } } 47 48 49 var nodo = root.childNodes.item(i);

27

28 29

30 31

32 33 34 35 // Carga el resultado del XML 36 function modificaContenido() { 37 if (http_request.readyState == 4) { 38 if (http_request.status == 200) { 39 vaciaContenido(); 40 41 var xmldoc = http_request.responseXML; 42 var root = xmldoc.getElementsByTagName('s alida').item(0); 43 44 var fondo = 0; 45 46 for(var i = 0; i < root.childNodes.length; i++){

var contenedor = document.getElementById("contenedor"); 50 var p = document.createElement("p"); 51 52 // Truco para los colores ;) 53 if (fondo == 0) { 54 p.setAttribute("id","linea0"); } 55 else { 56 p.setAttribute("id","linea1"); 57 } 58 fondo = 1 - fondo; 59 60 var titulo = document.getElementById("datos"); 61 p.textContent = nodo.firstChild.data; 62 63 contenedor.insertBefore(p,titu lo); 64 } 65 66 } else { 67 alert('Hubo un problema con la peticin.'); 68 } 69 } 70 }

58

Nmero 12

WWW.LINUX- MAGAZINE.ES

053-060_PythonL12

16.11.2005

9:02

Uhr

Pgina

59

053-060_PythonL12

16.11.2005

9:02

Uhr

Pgina

60

DESARROLLO Python

mente nada que ver, aparte del nombre. En nuestro ejemplo vemos tres funciones: hazPeticion() modificaContenido() vaciaContenido() Javascript, adems de muchas de las caractersticas presentes Figura 3: Nuestra pgina devolver los resultados de en otros lenguajes (y algunas la consulta sin recargar. ausentes) dispone de una coleccin de objetos que le permiten Nos referimos al ahora famoso realizar operaciones. A da de hoy los XMLHttpRequest. ms importantes: Si vemos el cdigo del Listado 3, vere Manipulacin DOM mos un lenguaje que quiz nos recuerda Manipulacin XML a Java. En realidad no tienen absoluta XMLHTTP DOM permite a Javascript manipular, en Listado 4: fichero estilo.css tiempo real, el contenido de la pgina web. Puede aadir, modificar o quitar etiquetas y atributos, por lo que pode01 #documento{ mos operar sobre el documento de cual02 margin-left: quier forma posible. Javascript puede 100px; manipular un fichero XML de igual 03 } forma que hace DOM. 04 Y la gran novedad, Javascript puede 05 realizar conexiones ASNCRONAS con el 06 #titulo{ servidor. Y resalto en mayscula la pala07 text-decoration: bra ASNCRONAS porque ah est la underline; clave. 08 } Esto significa que podemos hace cone09 xiones, traernos documentos XML del 10 servidor y realizar operaciones DOM o 11 de cualquier otro tipo, cuando quera12 #linea0 { mos! 13 margin: 0px; El usuario carga su pgina web, y una 14 padding-left: 20px; vez cargada, sin necesidad de recargarla, 15 background: #e0e0e0; podemos modificarla a nuestro antojo en 16 font-family: monosbase a informacin que podemos pedir pace; al servidor en cualquier momento. 17 } Volviendo a nuestras funciones, la fun18 cin hazPeticion() recibe una url, crea el 19 #linea1 { objeto XMLHttpRequest y despus de 20 margin: 0px; algunas comprobaciones, asigna una 21 padding-left: 20px; funcin para que sea invocada cuando el 22 font-family: monosfichero que esa url especifica sea compace; pletamente descargado. 23 } Esto significa que mientras leemos 24 nuestra web, Javascript estar bajando 25 #contenedor{ un fichero y, cuando finalice, llamar a 26 border-style: la funcin modificaContenido . dashed; Y qu hace esta funcin? Comprueba 27 border-width: el estado de la peticin, (el estado 200 el 1px; de todo correcto y el de 404 el de lo 28 width: 600px; sentimos mucho, pero el fichero solicita29 border-color: do no est disponible) y entonces obtieblack; ne el documento XML del objeto que 30 } gestionaba la conexin.

Pero antes invoca a vaciaContenido(), que localiza toda etiqueta con las ids linea0 o linea1 y las elimina de la pgina. Hacemos esto porque a continuacin las volvemos a introducir en la pgina, pero esta vez con los datos frescos del servidor. No queremos entrar en los detalles, ya que, en teora, esto es un artculo sobre Python no Javascript, pero bsicamente esto es lo que hace el fichero ajax.js. El fichero estilo.c, que aparece en el listado 4, simplemente configura los colores y caractersticas de algunas de las etiquetas, para que el aspecto mejore. Y se acab, aqu tenemos nuestra aplicacin AJAX. El resultado final ser el que vemos en la figura 3. Cuando pulsemos cualquiera de los botones, se cargar la salida de texto de la ejecucin asociada a cada uno de ellos en pantalla, pero sin recargar la pgina. Si queremos aadir un nuevo comando slo tenemos que introducir la lnea correspondiente en acciones en server.py y aadir un nuevo botn como los que ya existen en index.html.

Conclusin
Es tan complicado eso de AJAX? Por supuesto que no! Lo que ocurre es que es una palabra que se est convirtiendo en un mito, pero no deja de ser una astuta combinacin de programacin en el servidor y cliente adems del uso intensivo de XMLHttpRequest. Poco a poco AJAX est poblando todas las pginas webs y la mayora de los currculos vitae. Quien sabe, lo mismo dentro de dos aos esa palabra tenga tanto poder como otra palabra de cuatro I letras: J2EE.

60

Nmero 12

WWW.LINUX- MAGAZINE.ES

También podría gustarte