Introducción a AJAX

7.6. Aplicaciones complejas

7.6.1. Envío de parámetros mediante XML

La flexibilidad del objeto XMLHttpRequest permite el envío de los parámetros por otros medios alternativos a la tradicional query string. De esta forma, si la aplicación del servidor así lo requeire, es posible realizar una petición al servidor enviando los parámetros en formato XML.

A continuación se modifica el ejemplo anterior para enviar los datos del usuario en forma de documento XML. En primer lugar, se modifica la llamada a la función que construye la query string:

function valida() {
  peticion_http = inicializa_xhr();
  if(peticion_http) {
    peticion_http.onreadystatechange = procesaRespuesta;
    peticion_http.open("POST", "http://localhost/validaDatos.php", true);
    var parametros_xml = crea_xml();
    peticion_http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    peticion_http.send(parametros_xml);
  }
}

Seguidamente, se crea la función crea_xml() que se encarga de construir el documento XML que contiene los parámetros enviados al servidor:

function crea_xml() {
  var fecha = document.getElementById("fecha_nacimiento");
  var cp = document.getElementById("codigo_postal");
  var telefono = document.getElementById("telefono");

  var xml = "<parametros>";
  xml = xml + "<fecha_nacimiento>" + fecha.value + "<\/fecha_nacimiento>";
  xml = xml + "<codigo_postal>" + cp.value + "<\/codigo_postal>";
  xml = xml + "<telefono>" + telefono.value + "<\/telefono>";
  xml = xml + "<\/parametros>";
  return xml;
}

El código de la función anterior emplea el carácter \ en el cierre de todas las etiquetas XML. El motivo es que las etiquetas de cierre XML y HTML (al contrario que las etiquetas de apertura) se interpretan en el mismo lugar en el que se encuentran, por lo que si no se incluyen esos caracteres \ el código no validaría siguiendo el estándar XHTML de forma estricta.

El método send() del objeto XMLHttpRequest permite el envío de una cadena de texto y de un documento XML. Sin embargo, en el ejemplo anterior se ha optado por una solución intermedia: una cadena de texto que representa un documento XML. El motivo es que no existe a día de hoy un método robusto y que se pueda emplear en la mayoría de navegadores para la creación de documentos XML completos.

7.6.2. Procesando respuestas XML

Además del envío de parámetros en formato XML, el objeto XMLHttpRequest también permite la recepción de respuestas de servidor en formato XML. Una vez obtenida la respuesta del servidor mediante la propiedad petición_http.responseXML, es posible procesarla empleando los métodos DOM de manejo de documentos XML/HTML.

En este caso, se modifica la respuesta del servidor para que no sea un texto sencillo, sino que la respuesta esté definida mediante un documento XML:

<respuesta>
  <mensaje>...</mensaje>
  <parametros>
    <telefono>...</telefono>
    <codigo_postal>...</codigo_postal>
    <fecha_nacimiento>...</fecha_nacimiento>
  </parametros>
</respuesta>

La respuesta del servidor incluye un mensaje sobre el éxito o fracaso de la operación de validación de los parámetros y además incluye la lista completa de parámetros enviados al servidor.

La función encargada de procesar la respuesta del servidor se debe modificar por completo para tratar el nuevo tipo de respuesta recibida:

function procesaRespuesta() {
  if(peticion_http.readyState == READY_STATE_COMPLETE) {
    if(peticion_http.status == 200) {
      var documento_xml = peticion_http.responseXML;
      var root = documento_xml.getElementsByTagName("respuesta")[0];

      var mensajes = root.getElementsByTagName("mensaje")[0];
      var mensaje = mensajes.firstChild.nodeValue;

      var parametros = root.getElementsByTagName("parametros")[0];

      var telefono = parametros.getElementsByTagName("telefono")[0].firstChild.nodeValue;
      var fecha_nacimiento = parametros.getElementsByTagName("fecha_nacimiento")[0].firstChild.nodeValue;
      var codigo_postal = parametros.getElementsByTagName("codigo_postal")[0].firstChild.nodeValue;

      document.getElementById("respuesta").innerHTML = mensaje + "<br/>" + "Fecha nacimiento = " + fecha_nacimiento + "<br/>" + "Codigo postal = " + codigo_postal + "<br/>" + "Telefono = " + telefono;
    }
  }
}

El primer cambio importante es el de obtener el contenido de la respuesta del servidor. Hasta ahora, siempre se utilizaba la propiedad responseText, que devuelve el texto simple que incluye la respuesta del servidor. Cuando se procesan respuestas en formato XML, se debe utilizar la propiedad responseXML.

El valor devuelto por responseXML es un documento XML que contiene la respuesta del servidor. Como se trata de un documento XML, es posible utilizar con sus contenidos todas las funciones DOM que se vieron en el capítulo correspondiente a DOM.

Aunque el manejo de repuestas XML es mucho más pesado y requiere el uso de numerosas funciones DOM, su utilización se hace imprescindible para procesar respuestas muy complejas o respuestas recibidas por otros sistemas que exportan sus respuestas internas a un formato estándar XML.

El mecanismo para obtener los datos varía mucho según cada documento XML, pero en general, se trata de obtener el valor almacenado en algunos elementos XML que a su vez pueden ser descendientes de otros elementos. Para obtener el primer elemento que se corresponde con una etiqueta XML, se utiliza la siguiente instrucción:

var elemento = root.getElementsByTagName("nombre_etiqueta")[0];

En este caso, se busca la primera etiqueta <nombre_etiqueta> que se encuentra dentro del elemento root (en este caso se trata de la raíz del documento XML). Para ello, se buscan todas las etiquetas <nombre_etiqueta> del documento y se obtiene la primera mediante [0], que corresponde al primer elemento del array de elementos.

Una vez obtenido el elemento, para obtener su valor se debe acceder a su primer nodo hijo (que es el nodo de tipo texto que almacena el valor) y obtener la propiedad nodeValue, que es la propiedad que guarda el texto correspondiente al valor de la etiqueta:

var valor = elemento.firstChild.nodeValue;

Normalmente, las dos instrucciones anteriores se unen en una sola instrucción:

var tfno = parametros.getElementsByTagName("telefono")[0].firstChild.nodeValue;

Ejercicio 14

Normalmente, cuando se valida la disponibilidad de un nombre de usuario, se muestra una lista de valores alternativos en el caso de que el nombre elegido no esté disponible. Modificar el ejercicio de comprobación de disponibilidad de los nombres para que permita mostrar una serie de valores alternativos devueltos por el servidor.

El script del servidor se llama compruebaDisponibilidadXML.php y el parámetro que contiene el nombre se llama login. La respuesta del servidor es un documento XML con la siguiente estructura:

Si el nombre de usuario está libre: code.6beb5621364789ab1484a6918c1c98678f97bcd7

Si el nombre de usuario está ocupado: code.6c7f5737463989caea8a3e7bda25a5dc8ef1a231

Los nombres de usuario alternativos se deben mostrar en forma de lista de elementos (<ul></ul>).

Modificar la lista anterior para que muestre enlaces para cada uno de los nombres alternativos. Al pinchar sobre el enlace de un nombre alternativo, se copia en el cuadro de texto del login del usuario.

Descargar archivo ZIP con el script compruebaDisponibilidadXML.php

Ver solución

7.6.3. Parámetros y respuestas JSON

Aunque el formato XML está soportado por casi todos los lenguajes de programación, por muchas aplicaciones y es una tecnología madura y probada, en algunas ocasiones es más útil intercambiar información con el servidor en formato JSON.

JSON es un formato mucho más compacto y ligero que XML. Además, es mucho más fácil de procesar en el navegador del usuario. Afortunadamente, cada vez existen más utilidades para procesar y generar el formato JSON en los diferentes lenguajes de programación del servidor (PHP, Java, C#, etc.)

El ejemplo mostrado anteriormente para procesar las respuestas XML del servidor se puede reescribir utilizando respuestas JSON. En este caso, la respuesta que genera el servidor es mucho más concisa:

{
mensaje: "...",
parametros: {telefono: "...", codigo_postal: "...", fecha_nacimiento: "..." }
}

Considerando el nuevo formato de la respuesta, es necesario modificar la función que se encarga de procesar la respuesta del servidor:

function procesaRespuesta() {
  if(http_request.readyState == READY_STATE_COMPLETE) {
  if(http_request.status == 200) {
    var respuesta_json = http_request.responseText;
    var objeto_json = eval("("+respuesta_json+")");

    var mensaje = objeto_json.mensaje;

    var telefono = objeto_json.parametros.telefono;
    var fecha_nacimiento = objeto_json.parametros.fecha_nacimiento;
    var codigo_postal = objeto_json.parametros.codigo_postal;

    document.getElementById("respuesta").innerHTML = mensaje + "<br>" + "Fecha nacimiento = " + fecha_nacimiento + "<br>" + "Codigo postal = " + codigo_postal + "<br>" + "Telefono = " + telefono;
    }
  }
}

La respuesta JSON del servidor se obtiene mediante la propiedad responseText:

var respuesta_json = http_request.responseText;

Sin embargo, esta propiedad solamente devuelve la respuesta del servidor en forma de cadena de texto. Para trabajar con el código JSON devuelto, se debe transformar esa cadena de texto en un objeto JSON. La forma más sencilla de realizar esa conversión es mediante la función eval(), en la que deben añadirse paréntesis al principio y al final para realizar la evaluación de forma correcta:

var objeto_json = eval("("+respuesta_json+")");

Una vez realizada la transformación, el objeto JSON ya permite acceder a sus métodos y propiedades mediante la notación de puntos tradicional. Comparado con las respuestas XML, este procedimiento permite acceder a la información devuelta por el servidor de forma mucho más simple:

// Con JSON
var fecha_nacimiento = objeto_json.parametros.fecha_nacimiento;

// Con XML
var parametros = root.getElementsByTagName("parametros")[0];
var fecha_nacimiento = parametros.getElementsByTagName("fecha_nacimiento")[0].firstChild.nodeValue;

También es posible el envío de los parámetros en formato JSON. Sin embargo, no es una tarea tan sencilla como la creación de un documento XML. Así, se han diseñado utilidades específicas para la transformación de objetos JavaScript a cadenas de texto que representan el objeto en formato JSON. Esta librería se puede descargar desde el sitio web www.json.org.

Para emplearla, se añade la referencia en el código de la página:

<script type="text/javascript" src="json.js"></script>

Una vez referenciada la librería, se emplea el método stringify para realizar la transformación:

var objeto_json = JSON.stringify(objeto);

Además de las librerías para JavaScript, están disponibles otras librerías para muchos otros lenguajes de programación habituales. Empleando la librería desarrollada para Java, es posible procesar la petición JSON realizada por un cliente:

import org.json.JSONObject;
...
String cadena_json = "{propiedad: valor, codigo_postal: otro_valor}";
JSONObject objeto_json = new JSONObject(cadena_json);
String codigo_postal = objeto_json.getString("codigo_postal");

Ejercicio 15

Rehacer el ejercicio 14 para procesar respuestas del servidor en formato JSON. Los cambios producidos son:

1) El script del servidor se llama compruebaDisponibilidadJSON.php y el parámetro que contiene el nombre se llama login.

2) La respuesta del servidor es un objeto JSON con la siguiente estructura:

El nombre de usuario está libre: code.3d1e6360bcbd7143aa4cd480b2e43de5a59a4d2a js]{ disponible: "no", alternativas: ["...", "...", ..., "..."] }[/code

Descargar archivo ZIP con el script compruebaDisponibilidadJSON.php

Ver solución