Tantos proyectos y tan poco tiempo, entre la universidad y el trabajo me queda muy poco tiempo para trabajar con Caleio. Más si tenemos en cuenta que esta semana pasaba re-escribí mi CMS y rediseñé la página. Y por si esto no fuera poco estoy escribiendo un programa bastante importante en C# (no pregunten porque no voy a responder).

El punto es que quería escribir un artículo sobre Ajax. Ultimamente estuve usandolo mucho en el trabajo y con Caleio y me crucé con un problema interesante. La definición de Ajax en inglés es: Asynchronous JavaScript + XML según quien acuño el termino. Notese el XML del finál. Importante ese XML ya que todo el mundo parece ignorarlo… Investigando un poco más en dicha página aparece esta ‘lista’ de tecnologías que se incluyen en el concepto de Ajax:

  • standards-based presentation using XHTML and CSS;
  • dynamic display and interaction using the Document Object Model;
  • data interchange and manipulation using XML and XSLT;
  • asynchronous data retrieval using XMLHttpRequest;
    and JavaScript binding everything together.

Por alguna extraña razón NADIE que yo haya visto hasta ahora usa XML para pasar la información entre el servidor y el cliente. Es más, este problema surgió porque yo no sabía muy bien como hacerlo y buscaba un framework ya escrito para usarlo en Caleio, pero no encontré ninguno! Absolutamente ninguno, y eso que miré en varios. Esto fue antes de navidades así que capaz que la situación cambió pero en cualquier caso el 90% o más de la gente que usa Ajax no usa XML para pasar la información. El método que usa todo el mundo es el de hacer un REQUEST al servidor incluyendo la información en las variables GET o en el caso de lo más osados en las variables POST, a su vez el servidor responde con cachos de HTML que son mostrados en la página por medio de la propiedad innerHTML.

Esto hace que el código quede muy feo, que sea poco portable y limita el uso de la aplicación. Pero lo más importante es que viola totalmente la regla de separación de contenido y presentación. Tanto XHTML y CSS para después hacer esa guarrería :P

Al final conseguí un ‘framework’ bastante ligerito que recibía XML pero no lo enviaba, así que con un poco de manipulación conseguí lo que necesitaba. Es un script muy básico y en realidad no es un framework, lo único que hace es crear el objecto xmlhttprequest y luego enviar y recibir XML. Lo pueden encontrar en la sección de Code » tw-sack.js.

Así que ahí va un pequeño y corto tutorial de como hacerlo bien usando ese script. El ejemplo que usaré será una página que invierta el texto escrito en un input. El enfásis esta en como funcióna Ajax y no tanto en que se puede hacer en el servidor, es muy fácil modificar este ejemplo para que haga cosas mucho más complejas.

HTML + CSS

En primer lugar creamos una simple página HTML y le añadimos unos estilos con CSS. Para nuestro ejemplo solo necesitamos un input, un boton y un div donde mostrar el texto invertido.

<html>
	<head>
		<title>Ejemplo Ajax</title>
	</head>
	<body>
		<form action="#" method="post" onsubmit="return sendText()">
			<label for="texto">Texto:</label>
			<input type="text" id="texto" name="texto" /><br />

			<label for="submit"> </label>
			<input type="submit" id="submit" name="submit" value="Enviar" />
		</form>
		<p id="areaResultado"> </p>
	</body>
</html>

Falta el CSS y la inclusión de los archivos Javascript, pero se pueden dar una idea, nada complicado. Se podría no usar el form y llamar a la función sendText() desde el onClick() del botón, pero hecho así es más facil hacer la aplicacion compatible con los usuarios que no tengan activado Javascript ya que el formulario se ejecutará y ahí podemos hacer que el servidor se haga cargo como normalmente lo hace.

Javascript de ida

Así que el usuario ha apretado el boton y quiere saber como queda su texto al vesre. Creamos una función que saque el valor del campo, lo meta en el XML y lo mande.

function sendText()
{
	texto = document.getElementById('texto').value;

	xml = xml = '<?xml version="1.0" encoding="UTF-8" ?>'+
"<request>"+
	"<texto>""+texto+"</texto>""+
"</request>";

	ajax.object = document.getElementById('areaResultado');
	ajax.onCompletion = recieveText;
	ajax.runAJAX(xml);
}

Acá es donde hay una parte complicada, en la ante-última linea, donde pone ajax.onCompletion = recieveText. Esta parte es necesaria por la forma en que funcióna xmlhttprequest y Javascript. Cuando el script llega a la parte de enviar el XML no espera el resultado, sino que sigue la ejecución, asi que si fueramos a hacer:

	// crear xml
	ajax.runAjax(xml)
	alert(ajax.reponse);

Esto fallaría, porque el proceso de conseguir el resultado es largo y el Javascript no espera. Así que el truco es decirle al script que función queremos que ejecute después de haber conseguido el resultado, en este caso la función es recieveText(). Lo de ajax.object es para ‘guardar’ el objeto donde queremos luego insertar o modificar algo. Esto sirve más cuando a la función la llamamos con un ‘this‘ y navegamos por los nodos cercanos que posiblemente no se puedan o no querramos agarrar con un getElementById().

Pero primero el PHP

Antes de recibir el resultado hay que leer el texto con PHP y darlo vuelta. Cuando por primera vez llegué a esta parte me quede frito, cómo cuernos leo yo el XML en PHP? Y la solución es mucho más simple de lo que parece, yo ya estaba pensando en que iba a tener usar sockets de alguna manera extraña y seguramente depravada, pero no, resulta que la variable $HTTP_RAW_POST_DATA contiene, como bien indica su nombre, todo el contenido POST de manera ‘cruda’, es decir, sin procesar. Asi que leer el valor de texto es cuestion de parsear la variable como XML así:

<?php
	$xml = $HTTP_RAW_POST_DATA;

	$p = new xmlParser;
	$p->read($xml);
	$texto = $p->getTagValue('texto');

	header('Content-Type: text/xml');

	echo
'<'.'?xml version="1.0" encoding="utf-8" ?'.'>'.
'<response>'.
	'<texto>'.strrev($texto);.'</texto>'.
'</response>';
?>

Las lineas 4,5 y 6 hacen uso de una pequeña clase que escribí para parsear XML, es muy básica y la pueden encontrar acá. En cualquier caso, lo único que tienen que saber es que lee el XML y le saca el texto a la primera etiqueta <texto>. Usa principalmente xml_parse_into_struct que convierte el XML en un array, y de ahí en adelante es tan fácil como hacer un loop para buscar etiquetas, atributos y valores. Luego mandamos el header de text/xml, porque sino después xmlhttprequest no crea reponseXML, con lo cual no obtenemos las ventajas del DOM. Y finalmente escupimos el resultado usando strrev() para invertir la cadena.

Vuelta al Javascript

Antes de enviar el XML con Javascript había especificado que función quería que se ejecutase una vez que hubiese terminado de recibir la respuesta del servidor. Así que llegamos al paso final, leer el XML, sacar el valor de la cadena invertida y meterlo en #areaResultado.

function recieveText()
{
	xml = ajax.responseXML;
	texto = xml.getElementsByTagName('texto')[0].childNodes[0].data;
	
	ajax.object.innerHTML = texto;
}

Otra simple función, si es que esto del Ajax esta tirao. Primero agarramos el XML que nos devolvío el servidor, como ajax esta declarado como global podemos acceder desde cualquier función. Luego navegar por el árbol de XML es igual que hacerlo por el de HTML, solo que hay que añadir algunas cosillas más para llegar a la información, y finalmente asignamos el texto al innerHTML de #areaResultado que se asignó a ajax.object antes de enviar el XML. Era por mostrar esa funciónalidad nada más, tranquilamente podriamos esquivar ese paso y hacer el getElementById() en esta función.

<voz-del-flaco-de-bricolaje>
ya veis chavales, aqui tenemos nuestro propio sistemita Ajax, recordemos los pasos que hemos seguido:
</voz>

  1. Escribimos el HTML y añadimos la acción que llamará al Javascript
  2. Escribimos la primera función Javascript que envía el XML
  3. Escribimos el script PHP que procesa el XML y devuelve más XML
  4. Y finalmente escribimos la segunda función Javascript que nos permite leer el resultado y mostrarlo en el HTML

Es verdaderamente fácil una vez que uno se acostumbrar, pero para ayudar construí una demo con todos los archivos de este ejemplo. Que disfruten, y al próximo que vea que recibe HTML y lo mete asi nomás con innerHTML…

 

24 Responses to Ajax The Right Way

  1. Rodrigo says:

    Esto es AJAX completito, sí señor. No he probado el código, pero publicá tu clase para parsear XML, man :)

  2. Hermann says:

    Es una boludez, pero bueno, mañana la pongo.

  3. Daniel says:

    Buen articulo hermann, has probado JSON? resulta muy atractivo a la hora de enviar y recibir y mantiene la separación de contenido y diseño.No esta nada mal, si puedes echale un ojo.

  4. Hermann says:

    He escuchado mucho hablar sobre JSON, pero todavía no tuve tiempo de averiguar mucho. Tenía pensado investigar este fin de semana :P

  5. corsaria says:

    Vaya, peazo post. Aún sigo intentando comprender el primer párrafo. Quizás a esta gente les interesa. :-)

  6. Daniel says:

    Yo estoy trabajando con el y es una gozada, incluso puedes currarte una forma para poder trabajar con el mismo archivo que procesa sin AJAX.

  7. Hermann says:

    Yo tampoco lo entiendo :P

    No se que carajo quise decir… es malo doctor?

  8. pedro says:

    Muy interesante!
    Ya lo puse en mi lista de enlaces de consulta. Por ahora lo hago con innerHTML… XD

  9. corsaria says:

    Pues malo no es… pero…. tómate unas aspirinas por si acaso. :P

  10. Noatble post Hermann, solo un par de criticas constructivas.

    1. El parser XML de PHP4 es un chiste de mal gusto, aparte de ser procedural, es notablemente lento.

    2. Si usas PHP 5 no necesitas tu clase, tienes dicha funcionalidad presente por defecto , y tienes bastantes alternativas que tiene un performance razonable y son orientadas a objetos como XMLreader[1] , XMLWriter[2] , o DOM [3]

    3. la disponibilidad de $HTTP_RAW_POST_DATA depende de la directiva de configuración “always_populate_raw_post_data” la cual viene deshabilitada por defecto, es mucho mejor utilizar el wrapper php://input [4]

    4. estas confiando ciegamente en $HTTP_RAW_POST_DATA.(o en identico caso php://input) eso es una entrada de usuario, la cual debe ser validada, con un poquito de imaginacion, podriamos hacer bastantes travesuras con el ejemplo :-)

    [1]http://php.net/xmlreader
    [2]http://php.net/xmlwriter
    [3]http://php.net/dom
    [4]http://php.net/manual/en/wrappers.php.php

  11. algo asi te puede servir.

    pero se muy cuidadoso, el wrapper php://input bypasea allow_url_fopen y puede ser un simpatico vector de remote code execution :-) [1]

    http://www.securityfocus.com/bid/10427 ( como puedes ver, el problema fue descubierto en el año 2004, hasta el dia de hoy no hay solucion, simplemente por el hecho, de que no es un bug, es un feature ;-) , que provoca ese “side effect” )

  12. bueno, como ves en mi post anterior, tu blog devoró mi codigo :( vamos a ver si ahora funciona

  13. jorge says:

    he tratado de correr tu programa pero me marca un error de javascript en el xmlhttp.send(xml):

    Error: uncaught exception: [Exception… “Component returned failure code: 0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH) [nsIXMLHttpRequest.send]” nsresult: “0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH)” location: “JS frame :: file:///C:/Documents%20and%20Settings/Jorge%20Marin/Escritorio/tw-sack.js :: anonymous :: line 64” data: no]

  14. Hermann says:

    @judas_iscariote,
    1,2: Si ya se, la verdad es que no he investigado para ver si hay otros parsers de XML más eficientes. En cualquier caso en mi servidor solo tengo PHP 4 así que todas esas herramientas orientadas a objetos de PHP 5 están fuera de mi alcance.

    3: Había leido sobre los diversos wrappers de PHP, pero nunca se me ocurrió usarlos, investigaré un poco más teniendo en cuenta los detalles sobre seguridad que me comentás.

    4. Si si, ya se, en este caso no lo he hecho porque es un ejemplo muy simple y habría que buscar una vulnerabilidad en el parser XML o en strrev() para poder causar problemas (¿o estoy ignorando algo?)

    Gracias por los comentarios :P

    @Jorge: raro el error, esto te pasa al ejecutar el programa en el servidor o en tu escritorio?

  15. estas ingorando que eventualmente podria realizarze algun tipo de ataque XSS , y no creo que sea tan complicado, es cosa que ponga el codigo malicioso invertido en el text box y QUIZAS se pueda hacer algo entretenido.. ;-)

  16. konus says:

    Excelente acotacion!… yo pensaba que nadie mas se habìa dado cuenta de esto, al usar protoype.js por ejemplo, que facilita _todo_ menos el envio y recepcion de XML…. Una cosa inusual para una biblioteca tan usada.

    De todas formas tu ejemplo tiene un xml mal formado. Dice en la funcion sendText:
    “+
    “””+texto+”””+
    “”;

    Debe decir:
    “+
    “””+texto+”””+
    “”;

    Saludos!

  17. Hermann says:

    Mmmm, mi CMS saca todas las etiquetas no permitidas :P

    Si quieres escribir < y > usa las entidades HTML &lt; y &gt;

  18. Sebastián says:

    Hermann,
    recién descubro esto de Ajax y la verdad es que tengo una duda: porqué XML?
    o sea, ¿porqué gastarse en pasar los datos a XML, mandarlos al servidor para que ahí se parsee la info, procesarla, meterla de nuevo en XML y devolverla al cliente para que ésta tenga q parsearla nuevamente, cuando sería menos costoso enviar la info como texto plano, array o de cualquier otra forma que no me obligue a procesarla tantas veces?
    puede q sea un pregunta básica, o hasta estúpida, pero creo que necesito una una buena razón que me convenza para usar XML… ¿pido mucho? :)

    Saludos

  19. Hermann says:

    Eso es lo que opina mucha gente, para qué complicarse la vida?
    Yo lo hago por estandarizar las cosas, por ejemplo, creo que lo menciono en el artículo, uno de mis proyectos llamado Caleio usa el mismo API de la aplicación para AJAX y este como la mayoría de APIs usa XML, de esta manera me ahorro mucho trabajo.
    Lo del esfuerzo es un poco engañoso, tampoco es tan difícil tomarse ese paso extra de convertir a XML, en el Javascript no parseamos XML inicalmente, simplemente creamos una cadena que resulta ser XML, PHP y el resto de lenguajes que encontrarás por ahi tienen parseadores XML muy rápidos y eficientes, y parsear el resultado XML con Javascript es trivial.
    Para mi, de esta manera me aseguro que si otra gente quiere usar mi aplicación para mezclarla con otra les resulta fácil. Además a mi me parece que queda mas limpio y organizado asi.
    Por cierto, en uno de los comentarios anteriores se menciona JSON como formato alternativo a XML, te sugiero que le eches un vistazo también.

  20. Chicho says:

    Esta bueno esto de AJAX yo me hice una que otra aplicación usando esta metodología y les recomiendo ver tambien XSL para darle formato a los resultados, lo hacen con la clase XSLTProcessor.
    Por otro lado estoy buscando informacion de como llamar un webService desde AJAX, esto es mucho mejor que devolver XML en un simple PHP. Pod eso es que hay que usar XML con esta metodología. Te hacés un WebService y lo usas tanto desde tu aplicacion de escritorio como desde tu aplicacion web, lamentablemente no se como hacer esto, si alguien sabe por favor que lo postee. Saludos

    Chicho

  21. genius551v says:

    Primero q nada, gracias por la explicacion Tan Facil de entender, Tan ilustrativa, Tan bien explicada y en Español !

    Segundo, svoy a implementarlo si esto me funciona nunca tendre como pagarte, no te imaginas la desembarrada de la que me sacas.

    de antemano Gracias,

    genius551v

  22. Marco says:

    Buen Articulo felicidades, manos a la obra que somos muchos los que tamos con ganas de aprender

  23. AVDarie says:

    Hola:

    Primero de todo quería felicitarte por el artículo. Muy bien explicado y muy práctico.

    He querido poner en práctica dicho uso del AJAX y PHP pero me ha salido una complicación. El tema son los caracteres especiales “”.
    A primera vista todo se puede solucionar usando la función de PHP htmlspecialchars() o semejantes que convierte los caracteres epeciales a entidades HTML.

    Todo genial y creía que se me acabaría el problema. Pero no se que pasa que al recibir el cliente el XML y utilizamos la propiedad “xml.getElementsByTagName(‘texto’)[0].childNodes[0].data”,los caracteres especiales codificados anteriormente por el PHP aparece como “” y no “<”. Si hacemos un alert de la variable resposeText “respuesta en formato texto del server” aparecerá todos el XML impreso por el php y en esta variable si que respecta la codificación de los elemento HML realizados con el PHP.

    El mismo ejemplo que tienes en la página sufre de lo mismo si intentas escribir por ejemplo no funcionará correctamente.

    Esto ha sido probado con La version FIREFOX 2.0.0.7

    Saludos a todos

    Muy buen trabajo.

  24. fernando says:

    hola amigos y amigas tengo un problema y quiesiera que al gunos de eustedes me alludaran a
    realisar un codido de “php” en el cual quiero que en el texto escroba cual quier cosa y me la imprima en pantalla
    el monbre alreves ejemplo: fernando que lo imprima “odnanref”

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Set your Twitter account name in your settings to use the TwitterBar Section.