You are on page 1of 8

053-060_PythonL12 16.11.

2005 9:01 Uhr Página 53

Python • DESARROLLO

La nueva tecnología 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

más importante aún ¿qué

demonios es AJAX?

POR JOSÉ MARÍA RUIZ Y

PEDRO ORANTES

A
Brave New World” (“Un Mundo de campos o el arrastrar y soltar, que Esta fase ya casi ha pasado y ahora se
Feliz”) es el nombre de la famo- eran imposibles en la Web. busca la sencillez, y en el momento justo
sa novela de Aldous Huxley, en Conforme avanzaba el tiempo nume- surgió AJAX. Para más información ver
ella nos muestra un mundo distinto y rosas empresas y personas proponían url [2] de la tabla Referencias.
aterrador pero que parecía, y parece, soluciones. La lista es interminable:
cada vez más cercano. JavaScript, Java Applets, ActiveX, Tcl, Los problemas con IE
Nosotros no tenemos una visión tan VBScript, Macromedia Flash…
pesimista del mundo, pero es probable Pero todas fallaban de uno u otra Internet Explorer, a pesar de ser el pri-
que ese título (que se podría traducir manera. En el caso de Java, para ejecutar mero que introdujo XMLHTTPRequest,
literalmente por «un nuevo y desafiante el Applet necesitabas tener instalado el es el que más problemas da en su uso.
mundo») explique todo el revuelo que Java Runtime Environment, y la mayoría El código aquí mostrado ni siquiera fun-
está levantando AJAX. El término fue de los usuarios no sabían ni qué era ciona en IE debido a que en él se hace
acuñado por Jesse James Garrett en el aquello que se le pedía. Lo mismo ocu- uso de un componente ActiveX para
artículo [1] de la tabla Referencias. rría con Macromedia Flash. establecer la conexión.
Durante mucho tiempo las GUIs, las Lo peor era que cuando estaba solu- Existen numerosas técnicas para permi-
Interfaces Gráficas de Usuario, han cionado el tema de la instalación del tir la compatibilidad entre navegadores,
dominado la informática. La gente que software adecuado, los desarrolladores pero debido a la extensión del artículo y
trabajaba en la Web siempre estaba creaban, y crean, páginas horribles lle- a su complejidad no las hemos mostra-
do. El lector interesado en la compatibili-
intentando convencer a todo el mundo nas de cosas moviéndose que distraen e
dad puede estudiar el código de siste-
de que para la mayoría de los programas, irritan. Se sentían impulsados a usar
mas de código libre que implementan
un interfaz web bastaba. Pero los usua- hasta la última capacidad de las nuevas
AJAX como puede ser Sarissa. Vea la
rios estaban acostumbrados a ciertas herramientas y acababan generando referencia [3] en la tabla Recursos.
características, como el auto-completado monstruosidades.

WWW.LINUX- MAGAZINE.ES Número 12 53


053-060_PythonL12 16.11.2005 9:01 Uhr Página 54

DESARROLLO • Python

¿Pero qué es posibilidad de traer información al nave- ser consultado usando AJAX. Crearemos
AJAX? gador sin recargar la página. una web con algo de código Javascript
Muy buena pregunta. Lo Esto es útil para algunas tareas pero no que a intervalos accederá a nuestro ser-
cierto es que AJAX ha demasiado, ya que a nuestro puzzle le vidor Python y modificará el aspecto de
estado delante de nues- faltan piezas. La primera pieza es la la página web.
tras narices todo el adopción de esta librería por casi todos
tiempo, esperando a los navegadores, por lo tanto el código Los 5 Ingredientes
que alguna mente des- pasa a ser de aplicación universal. Los cinco ingredientes necesarios para
pierta lo redescubriese. Además resulta que podemos modifi- elaborar nuestro producto son CSS,
El acrónimo «AJAX» se car el contenido de la página en tiempo Javascript, HTML, XML y Python, como
compone de las pala- real usando el denominado árbol DOM. aparecen en la figura 1, y cada uno tiene
bras «Asynchronous Y por si fuese poco, cuando AJAX fue su función en esta obra.
JavaScript and XML», definido, los programadores comenzaron HTML es la base sobre la que vamos a
término acuñado por a usar protocolos XML para comunicarse trabajar, definimos una página web en la
Jesse James Garrett, y con los servidores. que todo ocurrirá. De hecho, con el paso
curiosamente su existen- ¿Qué quiere decir esto? Pues que del tiempo el propio HTML ha acabado
cia se debe a una de esas ahora, con AJAX, podemos cargar una convirtiéndose en una especie de planti-
famosas violaciones de los página y, sin tener que recargarla, traer- lla donde campan a sus anchas CSS y
estándares que suele realizar Microsoft nos información, modificar la página en Javascript.
con sus productos. tiempo real, e interactuar con servidores CSS nos permite otorgar propiedades
Allá por 1998, Microsoft introdujo den- remotos usando protocolos XML. visuales a los elementos de HTML.
tro de sus productos una librería que le Básicamente, una vez cargada la página Javascript es el encargado de actuar en
permitía hacer consultas usando el proto- web tenemos entre manos todas las posibi- la máquina cliente, en el navegador, y
colo HTTP de manera autónoma y asín- lidades de programación de un GUI tradicio- puede modificar tanto el HTML como las
crona. Cuando tu navegador accede a una nal. Y todo esto sin necesidad de plugins ni propiedades visuales que CSS define.
página y esta contiene código Javascript, instalaciones, toda esta tecnología está en Con la llamada XMLHttpResponse sus
este código a su vez puede traer informa- nuestros navegadores esperando ser usada. atribuciones se han disparado. Ahora se
ción de esa u otras páginas de manera ve como un lenguaje de programación de
independiente. Si además se hace que este ¿Cómo encaja Python? pleno derecho. En los próximos años
código permanezca en ejecución respon- Pues vamos a realizar un pequeño servi- puede que adquiera mucha más impor-
diendo a eventos tenemos entre manos la dor de contenidos en Python que pueda tancia de la que ha tenido hasta ahora.

Listado 1: fichero server.py


01 #!/usr/local/bin/python 19 "/uname.xml": envia_fichero(self,ruta,fiche-
02 ["envia_comando","uname -a"]} ro):
03 import BaseHTTPServer 20 33 # No usamos ruta, pero
04 import os 21 if self.path in accio- así simplificamos el código
05 import cgi nes.keys(): 34 p = Pagina(fichero)
06 22 accion = accio- 35 self.enviar_respues-
07 class AJAXHTTPRequestHandler nes[self.path] ta(p.tipo(), p.contenido())
(BaseHTTPServer.BaseHTTPReques 23 36
tHandler): (getattr(self,accion[0]))(self 37 def
08 """ .path,accion[1]) envia_comando(self,ruta,coman-
09 Responde a peticiones HTTP 24 else: do):
10 """ 25 if (self.path[-3:] 38 c = Comando(comando)
11 def do_GET(self): == ".js" or 39 self.enviar_respues-
12 "Gestiona los GET" 26 self.path[-4:] ta(c.tipo(), c.contenido())
13 == ".css"): 40
14 acciones = { 27 41 def enviar_respuesta(self,
15 "/" : self.envia_fichero("",self.pat tipo, contenido):
["envia_fichero","index.html"] h[1:]) 42
, 28 self.enviar_cabecera(tipo)
16 "/ps.xml" : 29 else: 43 self.wfile.write(con-
["envia_comando", "ps afx"], 30 tenido)
17 "/df.xml": self.envia_fichero("","404.htm 44
["envia_comando", "df"], l") 45 def enviar_cabecera(self,
18 "/who.xml": 31 tipo):
["envia_comando","who"], 32 def 46 self.send_respon-

54 Número 12 WWW.LINUX- MAGAZINE.ES


053-060_PythonL12 16.11.2005 9:01 Uhr Página 55
053-060_PythonL12 16.11.2005 9:01 Uhr Página 56

DESARROLLO • Python

XML es el nuevo lenguaje estándar de


intercambio de información.
Prácticamente cualquier lenguaje dispo-
ne ya de librerías para generar y analizar
documentos XML. Dentro del mundillo
AJAX se ha convertido en el estándar
para el intercambio de información con
el servidor y para la serialización 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 reco-
lectar información importante y confec-
cionar con ella ficheros XML.
Tenemos que compenetrar todos estos
elementos para realizar nuestro proyec- Figura 1: Esquema de nuestra aplicación AJAX con todos sus compenentes.
to. El objetivo es que los componentes
HTML, Javascript, CSS y XML sean tan mos BaseHttpRequest. Esta clase nos per- resa es el comando GET. Es el usado
estáticos como sea posible, debido a que mite construir servidores HTTP sin exce- para solicitar información al servidor.
todos ellos interactúan con el usuario. sivo esfuerzo, así que la emplearemos. Cada vez que se realice una petición GET
Es en el servidor Python donde debe- Pero no debemos usarla directamente, se invocará el método do_GET de
mos aportar la flexibilidad necesaria sino a través de la clase BaseHTTPRequestHandler así que lo
como para añadir nuevas características BaseHttpRequestHandler, que es la vamos a redefinir en nuestra clase.
sin tener que cambiar ninguno de los encargada de gestionar los eventos que Cuando se invoque do_GET, en la
otros elementos. se suceden en el servidor. Por tanto here- variable de instancia self.path se encuen-
daremos de ella y crearemos una clase tra la ruta solicitada por el cliente.
La parte de Python: llamada AJAXHttpRequestHandler, ver Nosotros contrastaremos esta ruta contra
BaseHTTPRequest Listado 1 (todos los listados de este artí- las que aceptamos. Si no se encuentra
Python dispone en sus librerías estándar culo pueden descargarse de [4]). entre ellas, devolveremos la célebre pági-
de muchos «esqueletos» para distintos Un servidor HTTP recibe distintos na 404, indicando que la página solicita-
tipos de servidores. Entre ellos encontra- tipos de comandos, pero el que nos inte- da no existe. La información se devuelve

Listado 1: fichero server.py (cont.)


se(200) 67 tipo = "xml"
47 68 elif (ext == ".css"):
self.send_header("Content-type 69 tipo = "css"
+ "</linea>"
","text/" + tipo) 70 return tipo
85 linea =
48 self.end_headers() 71
self.tuberia.readline()[:-1]
49 72 class Comando:
86 self.xml +=
50 class Pagina: 73 def __init__(self,coman-
"</salida>"
51 def __init__(self,nombre): do):
87 return self.xml
52 self.nombre = nombre 74 self.tuberia =
88
53 self.texto = "" os.popen(comando)
89 def tipo(self):
54 fichero = 75 self.xml = ""
90 return "xml"
file(self.nombre) 76
91
55 self.texto = 77 def contenido(self):
92 def test(HandlerClass =
fichero.read() 78 # fichero XML
AJAXHTTPRequestHandler,
56 fichero.close() 79 if not self.xml:
93 ServerClass =
57 80 self.xml = "<?xml
BaseHTTPServer.HTTPServer):
58 def contenido(self): version=\"1.0\" ?>"
94
59 return self.texto 81 self.xml +=
BaseHTTPServer.test(HandlerCla
60 "<salida>"
ss, ServerClass)
61 def tipo(self): 82 linea = self.tube-
95
62 tipo = "html" ria.readline()[:-1] # para
96 if __name__ == '__main__':
63 ext = self.nombre[-4:] quitar el \n
97 test()
64 if (ext == "html"): 83 while linea:
65 tipo = "html" 84 self.xml +=
66 elif (ext == ".xml"): "<linea>" + cgi.escape(linea)

56 Número 12 WWW.LINUX- MAGAZINE.ES


053-060_PythonL12 16.11.2005 9:01 Uhr Página 57

Python • DESARROLLO

usando el método self.wfile.write(), lo desde envia_respuesta antes de enviar el


que en él escribamos, será devuelto al fichero en sí. Es de esta manera como el
cliente que realizó la petición. navegador determina el tipo de fichero
Además del fichero index.html, ofrece- que recibe y sus características.
remos una serie de servicios en forma de Cuando arranquemos el servidor, vere-
ficheros XML. Estos servicios consistirán mos que van apareciendo mensajes
en la ejecución de un comando de siste- correspondientes a los distintos coman-
ma, y la conversión de su salida en un dos que se le mandan. La clase
fichero XML. El formato del fichero será Figura 2: La clase BaseHTTPRequest genera BaseHTTPRequest genera una entrada
muy sencillo: una entrada por comando. por cada una. Ver figura 2.

Listado 2: fichero <?xml version="1.0"?> Gestores de servicios


index.html <salida> Para gestionar los servicios se han crea-
<linea>linea de salida</linea> do las clases Pagina y Comando, que res-
01 <html> ... ponden a los mismos métodos. La prime-
02 <head> <linea>linea de salida</linea> ra, Pagina, se encarga de las peticiones
03 <title>Pruebas con ... de ficheros de texto, que en nuestro pro-
AJAX</title> <linea>linea de salida</linea> grama se reducen al fichero index.html y
04 <link rel="stylesheet" </salida> sus ficheros supletorios, fichero
href="estilo.css" Javascript y CSS. Básicamente los carga
type="text/css" /> Por lo que generaremos el fichero XML en una variable y, mediante el método
05 <script «a mano», sin hacer uso de librerías. De contenido, las demás clases tienen acce-
language="Javascript" esta manera, cuando el cliente solicite el sos a los mismos. En este caso, el pará-
06 src="ajax.js"> fichero ps.xml, nuestro servidor ejecuta- metro ruta no afecta a esta clase, pero se
07 </script> rá el comando ps afx, creará el fichero ha definido para que guarde compatibili-
08 </head> ps.xml con la salida del comando y se lo dad con la clase Comando.
09 <body> enviará al cliente. La clase Comando ejecuta el comando
10 <div id="documento"> especificado y permite el acceso al texto
11 <h3 Definición de servicios devuelto por él mismo mediante el
id="titulo">Información del Para permitir que la definición de servi- mismo método que la clase Pagina. De
sistema</h3> cios sea lo más simple posible, basta con esta manera son intercambiables.
12 <input type="button" introducir una nueva entrada en el dic- Ambas clases poseen un método tipo()
name="button" value="Procesos" cionario acciones de la clase que devuelve el tipo de fichero que alma-
13 AJAXHTTPRequestHandler con la cenan. En el caso de Comando, siempre
onclick="javascript:hazPeticio siguiente estructura: será un fichero XML, pero Pagina debe
n('ps.xml');" /> «adivinar» el tipo de los ficheros que
14 <input type="button" <ruta> : [<método_a_invocar>,U
name="button" value="Disco" <comando_a_ejecutar>],
15
onclick="javascript:hazPeticio Cuando se gestiona el comando
n('df.xml');" /> HTTP GET, se busca en este dic-
16 <input type="button" cionario la ruta. En caso de que
name="button" value="Usuarios" esté presente, se ejecutará el
17 método almacenado usando
onclick="javascript:hazPeticio como parámetros la ruta y el
n('who.xml');" /> comando. Esto nos da gran fle-
18 <input type="button" xibilidad, añadir un nuevo ser-
name="button" value="Máquina" vicio consiste en introducir
19 una nueva linea de código.
onclick="javascript:hazPeticio Existe un detalle importante,
n('uname.xml');" /> todo fichero devuelto usando
20 <div id="contenedor"> HTTP debe tener una cabecera
21 <div con una serie de líneas con formato
id="datos"></div> llave: valor que dan información al
22 </div> cliente, el navegador, sobre el fichero
23 </div> devuelto. Debido a problemas de
24 </body> espacio hemos decidido devolver sólo
25 </html> el formato del fichero. Para ello se
invoca el método envia_cabecera

WWW.LINUX- MAGAZINE.ES Número 12 57


053-060_PythonL12 16.11.2005 9:01 Uhr Página 58

DESARROLLO • Python

devuelve. Para ello se comprueba la este es uno de esos casos. Vayamos Javascript, desde otra
extensión de los mismos. En aras de la ahora a por AJAX para comprenderlo. perspectiva
simplicidad, sólo consideraremos tres Mucha gente ha tenido extraños encuen-
extensiones. HTML tros con este lenguaje de programación.
Cuando un comando es ejecutado por Quizá éste sea uno de los artículos Es raro y, hasta hace no demasiado, no
Comando, se tiene especial cuidado en donde Python tenga menor protagonis- muy útil. Te permitía modificar colores
utilizar la función cgi.escape sobre cada mo, pero con cinco actores suele ser en páginas web o poner insidiosos ban-
linea. Esta función realiza algunas con- complicado. Veamos el HTML. La pági- ners. Por no hablar de las famosas venta-
versiones dentro del texto que se le na index.html se puede ver en el nas emergentes.
pasa para que pueda ser correctamente Listado 2. Esto ha hecho que se haya ganado una
visualizado por un navegador web. El Básicamente carga un fichero fama muy mala, tal es así que casi todos
problema radica en que ciertos caracte- Javascript, un fichero CSS y muestra un tenemos restricciones en nuestro nave-
res, como pueden ser «<» or «”» son título junto a unos cuantos botones. gador en torno a qué acciones puede o
especiales y si no se «escapan», si no se Estos botones invocan acciones en no realizar Javascript. Lo más normal es
preceden de un «\», causarán proble- Javascript. que tengamos uno de esos famosos blo-
mas. Debemos fijarnos especialmente en el queadores de popups.
Y con esto, hemos definido un servi- uso del atributo id en numerosas etique- Pero Javascript se ha reinsertado en la
dor web básico. Python nos permite rea- tas HTML. Gracias a estos ids podremos sociedad de los programadores por la
lizar complejas tareas con poco código y manipularlas mediante Javascript. puerta grande gracias a un solo objeto.

Listado 3: fichero ajax.js.


01 // GLOBALES 25 47 var nodo =
02 var http_request = false; while(document.getElementById( root.childNodes.item(i);
03 "linea0") || 48
04 function hazPeticion(url) { 26 49 var contenedor = docu-
05 http_request = false; document.getElementById("linea ment.getElementById("contene-
06 http_request= new 1")){ dor");
XMLHttpRequest(); 27 nodo = 50 var p =
07 if document.getElementById("linea document.createElement("p");
(http_request.overrideMimeType 0"); 51
) { 28 if (! nodo){ 52 // Truco para los
08 29 nodo = colores ;)
http_request.overrideMimeType( document.getElementById("linea 53 if (fondo == 0) {
'text/xml'); 1"); 54
09 } 30 } p.setAttribute("id","linea0");
10 31 var nodo_basura = }
11 if (!http_request) { d.removeChild(nodo); 55 else {
12 alert('Error al crear la 32 } 56
instancia de 33 } p.setAttribute("id","linea1");
XMLHttpRequest.'); 34 57 }
13 return false; 35 // Carga el resultado del XML 58 fondo = 1 - fondo;
14 } 36 function modificaContenido() { 59
15 37 if (http_request.readyState 60 var titulo = docu-
16 // Esto es un callback, que == 4) { ment.getElementById("datos");
se dispara al terminar de 38 if (http_request.status == 61 p.textContent =
17 // descargar el fichero xml. 200) { nodo.firstChild.data;
18 http_request.onreadystate- 39 vaciaContenido(); 62
change = modificaContenido; 40 63
19 http_request.open('GET', 41 var xmldoc = contenedor.insertBefore(p,titu
url, true); http_request.responseXML; lo);
20 http_request.send(null); 42 var root = 64 }
21 } xmldoc.getElementsByTagName('s 65
22 // Elimina todo elemento con alida').item(0); 66 } else {
id "linea" 43 67 alert('Hubo un problema
23 function vaciaContenido(){ 44 var fondo = 0; con la petición.');
24 var d = 45 68 }
document.getElementById("con- 46 for(var i = 0; i < 69 }
tenedor"); root.childNodes.length; i++){ 70 }

58 Número 12 WWW.LINUX- MAGAZINE.ES


053-060_PythonL12 16.11.2005 9:02 Uhr Página 59
053-060_PythonL12 16.11.2005 9:02 Uhr Página 60

DESARROLLO • Python

mente nada que ver, aparte


del nombre. En nuestro
ejemplo vemos tres funcio-
nes:
• hazPeticion()
• modificaConte-
nido()
• vaciaContenido()
Javascript, además de muchas
de las características presentes
Figura 3: Nuestra página devolverá los resultados de en otros lenguajes (y algunas
la consulta sin recargar. ausentes) dispone de una colec-
ción de objetos que le permiten
Nos referimos al ahora famoso
realizar operaciones. A día de hoy los
XMLHttpRequest.
más importantes:
Si vemos el código del Listado 3, vere- • Manipulación DOM
mos un lenguaje que quizá nos recuerda • Manipulación XML Pero
a Java. En realidad no tienen absoluta- • XMLHTTP antes invoca a
DOM permite a Javascript manipular, en vaciaContenido(), que localiza toda eti-
Listado 4: fichero estilo.css tiempo real, el contenido de la página queta con las ids «linea0» o «linea1» y las
web. Puede añadir, modificar o quitar elimina de la página. Hacemos esto por-
01 #documento{ etiquetas y atributos, por lo que pode- que a continuación las volvemos a intro-
02 margin-left: mos operar sobre el documento de cual- ducir en la página, pero esta vez con los
100px; quier forma posible. Javascript puede datos frescos del servidor.
03 } manipular un fichero XML de igual No queremos entrar en los detalles, ya
04 forma que hace DOM. que, en teoría, esto es un artículo sobre
05 Y la gran novedad, Javascript puede Python no Javascript, pero básicamente
06 #titulo{ realizar conexiones ASÍNCRONAS con el esto es lo que hace el fichero ajax.js.
07 text-decoration: servidor. Y resalto en mayúscula la pala- El fichero estilo.c, que aparece en el
underline; bra ASÍNCRONAS porque ahí está la listado 4, simplemente configura los
08 } clave. colores y características de algunas de
09 Esto significa que podemos hace cone- las etiquetas, para que el aspecto mejore.
10 xiones, traernos documentos XML del Y se acabó, aquí tenemos nuestra aplica-
11 servidor y realizar operaciones DOM o ción AJAX.
12 #linea0 { de cualquier otro tipo, ¡cuando quera- El resultado final será el que vemos en
13 margin: 0px; mos! la figura 3. Cuando pulsemos cualquiera
14 padding-left: 20px; El usuario carga su página web, y una de los botones, se cargará la salida de
15 background: #e0e0e0; vez cargada, sin necesidad de recargarla, texto de la ejecución asociada a cada uno
16 font-family: monos- podemos modificarla a nuestro antojo en de ellos en pantalla, pero sin recargar la
pace; base a información que podemos pedir página. Si queremos añadir un nuevo
17 } al servidor en cualquier momento. comando sólo tenemos que introducir la
18 Volviendo a nuestras funciones, la fun- línea correspondiente en acciones en ser-
19 #linea1 { ción hazPeticion() recibe una url, crea el ver.py y añadir un nuevo botón como los
20 margin: 0px; objeto XMLHttpRequest y después de que ya existen en index.html.
21 padding-left: 20px; algunas comprobaciones, asigna una
22 font-family: monos- función para que sea invocada cuando el Conclusión
pace; fichero que esa url especifica sea com- ¿Es tan complicado eso de AJAX? ¡Por
23 } pletamente descargado. supuesto que no! Lo que ocurre es que
24 Esto significa que mientras leemos es una palabra que se está convirtiendo
25 #contenedor{ nuestra web, Javascript estará bajando en un mito, pero no deja de ser una astu-
26 border-style: un fichero y, cuando finalice, llamará a ta combinación de programación en el
dashed; la función modificaContenido. servidor y cliente además del uso inten-
27 border-width: ¿Y qué hace esta función? Comprueba sivo de XMLHttpRequest.
1px; el estado de la petición, (el estado 200 el Poco a poco AJAX está poblando todas
28 width: 600px; de «todo correcto» y el de 404 el de «lo las páginas webs y la mayoría de los
29 border-color: sentimos mucho, pero el fichero solicita- currículos vitae. Quien sabe, lo mismo
black; do no está disponible») y entonces obtie- dentro de dos años esa palabra tenga
30 } ne el documento XML del objeto que tanto poder como otra palabra de cuatro
gestionaba la conexión. letras: J2EE. ■

60 Número 12 WWW.LINUX- MAGAZINE.ES

You might also like