Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:programacion_avanzada_javascript:ajax

¡Esta es una revisión vieja del documento!


AJAX

Módulo perteneciente al curso Programación avanzada con JavaScript y ECMAScript.

Introducción

Desde sus comienzos y hasta hace relativamente poco tiempo las interfaces de usuario de las aplicaciones Web fueron más o menos siempre iguales. Las limitaciones propias del protocolo HTTP (Hyper Text Transfer Protocol) utilizado en las páginas Web han impuesto el tradicional modelo de “Petición-Respuesta-Procesado en el navegador” (a partir de ahora PRP) y vuelta a empezar.

Los pasos que sigue una aplicación Web convencional para funcionar suelen ser los siguientes:

  1. El usuario solicita una página.
  2. El servidor devuelve el contenido HTML correspondiente a ésta, normalmente generado a partir de alguna tecnología de servidor (como ASP.NET o PHP).
  3. El navegador recibe este HTML, lo procesa y visualiza el contenido resultante.
  4. El usuario interactúa con el HTML, envía un formulario al servidor o pulsa un enlace, y se repite el ciclo desde el paso 1: se solicita la página, se devuelve su contenido, se procesa y se visualiza.

Este proceso es el más natural para HTTP que estaba pensado para funcionar así cuando se concibió. El problema es que cualquier acción en la página que requiera apoyo del servidor implica solicitar otra página completa, descargarla y procesarla, lo cual es percibido de manera evidente por los usuarios (es decir “se nota” el refresco de la página) y crea una sensación poco amigable.

Además, si el retorno de la página tarda más que unos pocos milisegundos se pierde capacidad de respuesta de la interfaz puesto que, durante el proceso de petición-respuesta-procesado, la zona de visualización del navegador no responde.

La tendencia actual en todos los desarrollos web, sin embargo, es crear aplicaciones y páginas cada vez más parecidas a aplicaciones de escritorio, desdibujando las fronteras entre la Web y los programas que se ejecutan en el equipo local. Esto implica que los molestos y a la vez inevitables viajes al servidor no deberían ser percibidos por los usuarios y que la interfaz de la página debe responder en todo momento, jamás bloquearse. La sensación para los usuarios debe ser la de que la aplicación está todo el tiempo en su equipo, dejando de lado al servidor, como en una aplicación de escritorio tradicional. Seguro que has utilizado alguna vez GMail, Outlook.com, Facebook o alguna otra aplicación web reciente, así que sabes perfectamente de qué estoy hablando.

AJAX

Ya hemos aprendido a lo largo de este curso las posibilidades que ofrecen los navegadores actuales para trabajar en el lado cliente con HTML, CSS y JavaScript. La tendencia imparable en las aplicaciones web es la de llevar cada vez más procesamiento y lógica al navegador, dejando el servidor para procesar las reglas de negocio y el acceso a datos.

Dado que las páginas se recargan cada vez menos pero las aplicaciones web necesitan comunicarse con el servidor, se necesita algún mecanismo para poder hacerlo de manera transparente al usuario. Ese mecanismo se denomina de manera genérica AJAX.

Este simpático acrónimo hasta hace poco asociado con el mundo de la limpieza y el fútbol, viene del acrónimo en inglés Asynchronous JavaScript And XML. Se basa en el uso de un objeto llamado XMLHttpRequest o abreviadamente XHR que está presente en todos los navegadores modernos. Como es fácil imaginar por su nombre, sirve para realizar peticiones de documentos XML al servidor a través del protocolo HTTP. La idea original era que, utilizando este objeto se solicitan al servidor datos en formato XML que, una vez recibidos en el navegador, es posible manipular mediante código JavaScript, mostrando los resultados al modificar dinámicamente los elementos de la página. Como comprobaremos en breve, desde este punto inicial a lo que AJAX implica actualmente, las cosas han cambiado mucho.

Aunque casi todo el mundo se piensa que AJAX es un invento de Google y su potente cliente Web de correo electrónico GMail (primera aplicación que en verdad lo popularizó entre el público general), el objeto XMLHttpRequest apareció originariamente junto a las bibliotecas XML de Microsoft (MSXML) a finales de los años noventa del siglo pasado. El concepto original de esta tecnología fue creado también por Microsoft (se Llamaba Remote Scripting), el primer navegador en soportarlo fue Internet Explorer y la primera aplicación de este tipo fue Outlook Web Access (OWA), para acceder a buzones Exchange.

Como veremos enseguida, aparte de que su nombre original haya perdurado por ser simpático, la realidad es que AJAX no siempre es asíncrono y casi nunca usa XML. De hecho, lo más habitual es que hoy en día utilice otro formato para transferir los datos: JSON, que ya hemos estudiado en el módulo de programación orientada a objetos. Lo que de verdad constituye el corazón de esta técnica es el objeto XMLHttpRequest.

JSON (JavaScript Object Notation): es un formato estándar para definición de objetos en este lenguaje que es fácil de leer para humanos y fácil de interpretar para máquinas, y que utiliza parejas de clave-valor para definir los objetos. Se usa hoy en día como el principal formato de intercambio de datos entre servidores y aplicaciones web, como alternativa al más complejo y pesado XML.

En este módulo vamos a estudiar los fundamentos de la tecnología para que no dependas de biblioteca alguna a la hora de implementar estas características, y sobre todo -seamos realistas, casi nunca usarás esto “a pelo”- para que cuando uses una de dichas bibliotecas comprendas lo que hay debajo y puedas determinar posibles problemas.

Conociendo bien los conceptos subyacentes tendrás muchas más herramientas para sacarle partido a las bibliotecas construidas sobre ellos, como por ejemplo jQuery, AngularJS, EmberJS, etc…

El objeto XMLHttpRequest

Para sacar partido a AJAX, aparte de tener conocimientos de HTML y JavaScript, el primer objeto que debemos conocer a fondo es el mencionado XMLHttpRequest.

Se trata de una clase disponible en todos los navegadores modernos que permite lanzar desde JavaScript peticiones de recursos GET Y POST a través de HTTP.

Dicho en lenguaje simple, lo que esta clase nos permite es simular mediante código JavaScript llamadas al servidor como si éstas hubieran sido hechas por los usuarios. El efecto es el mismo que si el usuario hubiese enviado un formulario o pulsado sobre un enlace, sólo que nuestro código es el que tiene el control sobre ello y lo gestiona de manera paralela al contenido actual de la página (en la jerga se suele decir que lo gestiona “por debajo” o “por detrás”).

Los métodos y propiedades básicos de esta clase que debemos conocer son los siguientes (los corchetes indican parámetros opcionales):

  • open(metodo, URL, [asincrono], [usuario], [clave]): sirve para abrir una conexión al servidor. No envía ni obtiene información, sólo se conecta. El primer parámetro indica de qué manera queremos enviar la petición al servidor: normalmente por GET o por POST, pero en general, mediante cualquier otro verbo HTTP admitido por el servidor. El segundo es la dirección Web a la que vamos a llamar. El tercer parámetro es booleano y sirve para indicar si la conexión se realizará de forma asíncrona (por defecto) o no. Los dos últimos parámetros sirven para especificar un nombre de usuario y una contraseña de acceso para recursos protegidos por autenticación básica, si bien esto es bastante absurdo pues estarán escritos en claro en el JavaScript, por lo que raramente se utilizan.
  • send(contenido): envía una petición abierta con el método anterior. Si es un envío por POST se pueden incluir los datos a enviar en su único parámetro. Si no hay nada que enviar se usa un nulo.
  • abort(): cancela un envío/petición abierto previamente.
  • onreadystatechange: se trata de un evento idéntico al que tenemos para la carga de documentos HTML. Se le asigna una referencia a un método JavaScript que será llamado automáticamente cuando se termine la descarga de la URL remota a la que llamamos asíncronamente con los métodos anteriores.
  • readyState: informa del estado de la petición:
    • 0 = no iniciada
    • 1 = cargando
    • 2 = terminada pero sin procesar
    • 4 = completada
  • status: código de estado HTTP resultado de la petición. Por ejemplo 200 (éxito), 404 (no encontrado), 500 (error interno), etc… Se corresponden con los códigos de estado estándar de HTTP.
  • statusText: mensaje de información correspondiente al estado anterior.
  • responseXML: jerarquía DOM que representa el XML devuelto por la petición. Este DOM es igual al que hemos aprendido a utilizar para una página Web, sólo que actúa sobre un documento XML genérico. Esta propiedad sólo es útil si lo que nos devuelve el servidor es un documento XML. Lo habitual es que no lo utilicemos casi nunca pues el formato XML se ha abandonado casi por completo a favor del formato JSON.
  • responseText: el contenido puramente textual del recurso remoto. Es la que usaremos habitualmente para obtener el contenido del recurso solicitado.

Aunque dispone de algunos métodos y propiedades más, con estas tendremos suficiente para el 99% de los casos que nos vamos a encontrar.

Vamos a ver cómo funciona en la práctica con un ejemplo sencillo pero directo.

NOTA: este curso se centra exclusivamente en JavaScript y tecnología de lado cliente. Por ello, aunque en aplicaciones con AJAX se suele utilizar siempre algún tipo de programa de servidor que devuelve información, no es objeto de este curso aprender a crear este tipo de servicios, que requerirían realmente un curso en sí mismo. Así que deberás aprender a crear servicios usando la tecnología de servidor que prefieras: ASP.NET, ASP.NET MVC/Web API, PHP, NodeJS, Phython… También puedes utilizar alguna API de terceras partes que te proporcione la información. Veremos un ejemplo posteriormente.

DEMO: Uso básico de XMLHttpRequest

Este ejemplo está hecho en su parte servidora con ASP.

El HTML con el JavaScript:

<!DOCTYPE html>
<html>
    <head>
	<title>Ejemplo de autocompletar con AJAX</title>
	<script type="text/javascript">
 
            var httpReq;    //objeto para realizar peticiones HTTP
	    var items;	    //Contendrá el XML obtenido para los elementos de la lista
 
            //Se encarga de cargar los items de la lista secundaria
	    function cargaItems(tipo)
            {
		getXML("damesecundario.asp?tipo=" + tipo);
	    }
 
            function getXML(url)
	    {
		// Soportado por navegadores antiguos
		if (window.XMLHttpRequest)
		{
		    httpReq = new XMLHttpRequest();
		}
		else	//Internet Explorer 8 y anteriores lo expone como control Active X
		{
		    httpReq = new ActiveXObject("Microsoft.XMLHTTP");
		}
 
		//Ya debería tener una referencia al objeto
                if (httpReq != null)
		{
		    httpReq.onreadystatechange = finCarga;
		    httpReq.open("GET", url, true)	//El true del final es para que lo solicite de forma asíncrona
		    httpReq.send(null);	//al ser una petición GET no va nada en el cuerpo de la petición, de ahí el null
		}
	    }
 
	    //Función que se ejecuta cuando cambia el estado de la carga del objeto httpReq
	    function finCarga()
	    {
                if (httpReq.readyState == 4)	//4: completado, 3: en curso, 1:cargado, 0: no iniciado
		{
		    if (httpReq.status == 200)	//200: OK (código HTTP, podría haber sido 404 (no encontrado), 500 (error), etc...)
		    {
			items = httpReq.responseXML.getElementsByTagName("item");	//Coge sólo los elementos "item"
		        ProcesaItems();
		    }
		    else	//Se produjo un error
		    {
			alert("No se pudo recuperar la información de la lista secundaria: " + httpReq.statusText);
		    }
                }
	    }
 
	    function ProcesaItems()
	    {
		listaSec = document.getElementById("mnuSecundario");
		if (listaSec != null)
		{
		    listaSec.options.length = 0;	//Vacía la lista de opciones
		    //Cargamos las nuevas opciones
		    for(i=0; i<items.length; i++)
		    {
			var opc = document.createElement("OPTION");
			opc.text = items[i].childNodes[0].nodeValue;
			opc.value = items[i].childNodes[0].nodeValue;
			listaSec.options.add(opc);
		    }
		}
	    }
        </script>
    </head>
    <body>
	<a href="http://www.campusmvp.com">
	<img src="campusmvp.jpg" style="border-width: 0px" /></a>
	<p><b>Ejemplo</b> de <b>auto-completar</b> din&aacute;micamente desde el servidor
	<b>con AJAX</b></p>
	<p>Selecciona un elemento de la primera lista y ver&aacute;s como se rellena 
	<br>din&aacute;micamente el contenido de la segunda con AJAX desde el servidor:</p>
	<form name="form1">
	    <p><select name="mnuPrimario" id="mnuPrimario" size="1" onchange="cargaItems(this.value);">
	        <option value=""></option>
                <option value="revistas">Revistas</option>
                <option value="blogs">Blogs</option>
                <option value="empresas">Empresas</option>
                <option value="libros">Libros</option>
              </select>
              <select name="mnuSecundario" id="mnuSecundario" size="1">
                  <option value="">[Sin elementos]</option>
	      </select></p>
	</form>
	<p>&nbsp;</p>
    </body>
</html>

La anterior página web es un formulario con dos selectores. Lo que haces es que cuando se elige algo en el primer selector, en la segunda lista aparecerán más opciones dependiendo de lo que hayamos elegido.

Para la segunda lista de opciones, se hace una consulta usando AJAX a un script hecho en ASP.

El fichero ASP:

<%
	Dim items
	Select Case LCase(Request("tipo"))
		Case "revistas"
			items = Array("PC World", "MSDN Magazine", "CodeProject")
		Case "blogs"
			items = Array("www.campusmvp.com", "www.jasoft.org", "jmalarcon.es")
		Case "empresas"
			items = Array("Krasis [www.krasis.com]", "Microsoft [www.microsoft.com]", "Plain Concepts [www.plainconcepts.com]")
		Case "libros"
			items = Array("Crimen y castigo", "Cien años de soledad", "El Quijote")
	End Select
 
	'Se devuelve como XML
	Response.ContentType = "text/xml"
	'Se evita que haya caché de este contenido devuelto
	Response.AddHeader "cache-control", "no-cache"
	Response.Expires = -1
 
	'Se devuelve el XML
	Response.Write DevuelveMatriz(items)
 
'Esta función se encarga decrear el XML apropiado
Function DevuelveMatriz(arrItems)
	Dim sRes
	sRes = "<?xml version=""1.0"" encoding=""ISO-8859-1"" ?>" & vbCrLf & "<items>"
 
	If IsArray(arrItems) Then
		'Se recorre la matriz para construir el XML
		For Each item In arrItems
			sRes = sRes + "<item>" + item + "</item>"
		Next
	Else
		sRes = sRes + "<item>[Sin elementos]</item>"
	End If
 
	sRes = sRes + "</items>"
 
	DevuelveMatriz = sRes
End Function
%>

Envío de datos al servidor

Normalmente cuando pensamos en AJAX, es decir, en llamadas asíncronas a servicios, lo hacemos desde el punto de vista de obtener información: llamo a una página que me devuelve unos valores y los muestro en la interfaz de usuario. Es lo que acabamos de ver en el vídeo anterior.

Aunque este es el uso que primero viene a la mente para AJAX, lo cierto es que se utiliza tanto o más en el sentido inverso: para enviar datos al servidor. Con eso podemos actualizar un registro, enviar una orden, ordenar el borrado de un dato, o incluso subir un archivo.

La forma más sencilla y directa de enviar datos simples al servidor es incluirlos en la URL a la que llamamos como parámetros GET:

urldestino.aspx?Parametro1=1234&Parametro2=5

GET: Método HTTP que envía la información de un formulario en el propio URL de destino de éste, añadiéndolos al final como parámetro del “QueryString” (lo que va después de la interrogación.

Aunque esto puede servirnos para cosas muy sencillas no es lo que necesitaremos en la mayor parte de los casos.

Lo habitual es que la información haya que enviarla con el método POST. La principal diferencia entre GET y POST estriba en que el método GET hace una sola llamada al servidor, solicitando una página y enviando algunos parámetros de datos en la propia petición. POST por el contrario solicita la URL pero además envía los datos en el cuerpo de la petición, como contenido.

Por GET lo máximo que se puede enviar son 2 kB de información, mientras que por POST no existe esta limitación.

POST: Método HTTP que envía la información usando una petición en la que los datos del formulario viajan adjuntos dentro del contenido de la propia petición, por lo que no son visibles para el usuario.

Para enviar datos al servidor mediante POST nuestro código AJAX sería similar al siguiente:

http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open("POST", "http://www.miserv.com/misdatos.aspx", true)
http.send('Parametro1=1234&Parametro2=5');

Con esto no hemos ganado demasiado. Ahora se envían los datos por POST (sólo cambia el primer parámetro de open) pero los hemos tenido que introducir en el método send en lugar de en la propia URL. Esto sólo simularía el envío de parámetros mediante POST desde un formulario HTML, aunque por otro lado en ocasiones puede ser lo que queramos.

Lo habitual sin embargo es que, en lugar de enviar parámetros, queramos enviar información arbitraria del tamaño que sea preciso, que es para lo que suele usarse POST (por ejemplo, un objeto JSON completo con información). Esto se puede conseguir modificando ligeramente el código anterior para incluir una cabecera que indique al servidor que lo que le llega son, precisamente, datos (línea 3 del siguiente fragmento alternativo):

http = getHttpRequest()
http.onreadystatechange = finCarga;
http.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
http.open("POST", "http://www.miserv.com/misdatos.aspx", true)
http.send('Aquí ahora mando la información que quiera al servidor');

De esta manera podremos enviar información en formato JSON (luego lo veremos), los campos serializados de un formulario, o los contenidos de un archivo.

Problemas típicos de AJAX y cómo resolverlos

Devolución de información JSON

JSONP para acceso a otros dominios

Control de acceso entre dominios: CORS

informatica/programacion/cursos/programacion_avanzada_javascript/ajax.1728895777.txt.gz · Última modificación: por tempwin