sábado, 26 de mayo de 2012

Cómo usar XMLHttpRequest y XDomainRequest para contenido web dinámico

El uso de DHTML para la creación de contenido web dinámico cada vez es más común en la red, de hecho, ha sepultado prácticamente el diseño de portales estáticos.

Asumo, por lo visto en diversos foros y por experiencias propias vividas, que al adentrarnos en la codificación de contenido dinámico, todos tropezaremos con la misma piedra: el uso de XMLHttpRequest, que, como ya sabemos (o deberíamos saber), es un objeto para intercambiar datos "detrás de cámaras" con un servidor.

Pasaremos por alto los detalles y/o especificaciones sobre este objeto pues considero que ya hay suficientes en la red y son fáciles de encontrar, en caso de que estén interesados en conocerlo profundamente.

Entremos de lleno al tema y recordemos que, aunque parezca tonto decirlo, este objeto se implementa dentro de javascript y su uso, en líneas generales, es de la siguiente forma:

if (window.XMLHttpRequest) {
  // código para IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
}
else {
  // código para IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","URI_del_archivo",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;

Deberemos tener en cuenta que URI_del_archivo debe cumplir con la Política del Mismo Origen (Same Origin Policy), en pocas palabras, el archivo que deseas obtener usando XMLHttpRequest debe estar alojado en el mismo servidor desde el cual se está haciendo la solicitud. En caso de no ser así, el servidor al cual estás solicitando el archivo debe dar una respuesta que incluya la cabecera HTTP Access-Control-Allow-Origin: URIs, donde URIs debe incluir (para dar permisos al navegador) al directorio web que está realizando la solicitud. Daremos un ejemplo de esto:

Supongamos que nuestra página web está alojada en http://misitioweb.com y queremos realizar una solicitud al archivo nombres.xml que se encuentra en otrositioweb.com, es decir, la URI completa sería http://otrositioweb.com/nombres.xml. En este caso, como son diferentes dominios, otrositioweb.com deberá incluir en su respuesta la cabecera HTTP Access-Control-Allow-Origin que otorgue permisos de manipulación de datos a misitioweb.com, esto podría presentarse de varias formas, señalaremos dos de ellas, las más sencillas:

1:  Access-Control-Allow-Origin: http://misitioweb.com
2:  Access-Control-Allow-Origin: *

Si la cabecera anterior incluye a misitioweb.com, como en los dos casos anteriores, entonces podremos solicitar, descargar y manipular sin inconvenientes los datos solicitados con XMLHttpRequest, de lo contrario, sería imposible realizar esta operación directamente y deberemos recurrir a otros métodos, por ejemplo, un servidor proxy que anule las restricciones de mismo origen o same origin, como queramos llamarlo.

Ahora bien, esperando que todo esté claro hasta este punto, deberemos tener en cuenta otro detalle: la inclusión de un nuevo objeto por parte la versión 8 en adelante de Internet Explorer. Sí, esto quiere decir que Microsoft ha roto nuevamente los estándares mediante IE.

Si nos preocupa la compatibilidad de nuestro sitio dinámico con todos los navegadores (o la mayoría) entonces debemos preocuparnos por lo anterior.

Para solicitudes dinámicas cruzadas (crossdomain requests) utilizando IE>7 incluiremos el objeto XDomainRequest de Microsoft, cuyo uso es similar al XMLHttpRequest:

xmlhttp = new XDomainRequest();
xmlhttp.onload=function() {
  xmlDoc=xmlhttp.responseText;
}
xmlhttp.open("GET", "URI_del_archivo");
xmlhttp.send();

Es importante señalar que XDomainRequest sólo nos suministrará respuestas del tipo texto (responseText) y que si queremos un objeto XML como respuesta tenemos que utilizar la función de IE loadXML (busca en google para más detalles).

Finalmente podemos unir todo en un mismo segmento de código para realizar solicitudes a terceros desde nuestra página. Tendríamos algo así:

if (window.XDomainRequest) {
  xmlhttp = new XDomainRequest();
  xmlhttp.onload=function() {
    xmlDoc=xmlhttp.responseText;
  }
  xmlhttp.open("GET", "URI_del_archivo");
  xmlhttp.send();
}
else {
  if (window.XMLHttpRequest) {
    xmlhttp=new XMLHttpRequest();
  }
  else {
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
  xmlhttp.open("GET","URI_del_archivo",false);
  xmlhttp.send();
  xmlDoc=xmlhttp.responseText;
}

Nótese que el argumento "GET" puede también ser "POST" y que XDomainRequest sólo acepta transmisiones de datos asíncronas, a diferencia de XMLHttpRequest que acepta tanto síncronas como asíncronas.

El método utilizado en este segmento de código es algo genérico, podría mejorarse estructuralmente y adaptarse a cualquier requerimiento, sólo sienta las bases para la transmisión de datos HTML dinámicamente a través diferentes dominios intentando obtener la máxima compatibilidad con todos los navegadores.