Hace algún tiempo hice un tutorial de Ajax con jQuery, JSON y PHP. Hoy vengo con lo mismo pero sin jQuery, es decir, vamos a ver como trabajar con Ajax y JSON utilizando JavaScript puro, sin ningún framework ni biblioteca. Volvemos a los orígenes de tecnología Ajax: crear un objeto XMLHttpRequest con el que JavaScript puede comunicarse con el servidor de forma asíncrona intercambiando datos entre el cliente y el servidor sin interferir en el comportamiento actual de la página. Comúnmente, la respuesta desde servidor es utilizada para realizar manipulaciones posteriores en el DOM; por ejemplo, cambiar el contenido mostrado en la página.

Construyendo el objeto XMLHttpRequest y enviando la solicitud

El objeto XMLHttpRequest es el responsable de comunicarse con el servidor de forma asíncrona. Crear este objeto necesita, como mínimo, la URL que se va a solicitar al servidor, la cuál puede ser cualquier tipo de recurso, y no sólo XML (aunque su nombre sea XMLHttpRequest). Por ejemplo, la URL solicitada podría devolver HTML, JavaSript, CSS, texto plano, imágenes o, lo que nos ocupa en este tutorial, JSON.

Ejemplo con método GET

En el método GET, los parámetros de la solicitud se añaden a la URL como query string, es decir, se añade a la URL una cadena del tipo param1=value1&param2=value2:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definimos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Añadimos los parámetros a la URL
ajax_url += '?' + params;

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos como queremos realizar la comunicación
ajax_request.open( "GET", ajax_url, true );

//Enviamos la solicitud
ajax_request.send();

Parémonos un momento en el método XMLHttpRequest.open. Este método admite varios parámetros, que son: (os remito a la documentación para más informatión3)

  • método: el método HTTP a utilizar en la solicitud Ajax. GET o POST, en el ejemplo anterior fue GET.
  • url: dirección URL que se va a solicitar, o lo que es lo mismo, la URL a la que se va enviar la solicitud.
  • async: true (asíncrono) o false (síncrono). Es opcional y el valor por defecto es true. Hay quien recomienda ponerlo siempre por si no se respeta el valor por defecto.

Ejemplo con método POST

Si se utiliza el método POST, la solicitud necesita ser enviada como si fuera un formulario (Content-type: application/x-www-form-urlencoded o multipart-form-data). Los datos se envían, al igual que antes en el ejemplo con el método GET, como query string pero en este caso se envían como parámetro del método XMLHttpRequest.send y no como parte de la URL:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definimos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos como queremos realizar la comunicación
ajax_request.open( "POST", ajax_url, true );
// Ponemos las cabeceras de la solicitud como si fuera un formulario, necesario si se utiliza POST
ajax_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//Enviamos la solicitud junto con los parámetros
ajax_request.send( params );

Enviar una solicitud POST con JSON

Había dicho que el formato de los datos enviados en la solicitud POST tiene que ser tipo query string (key=valor&key2=valor2) y que se tienen que envíar como un formulario (Content-type: application/x-www-form-urlencoded). Bien, no es cierto.

En realidad podemos hacer un POST con los datos en cualquier formato, por ejemplo en JSON:

// El JSON a enviar
var myjson = '{ "key" : "value", "key1" : "value1", "key2" : "value2" }'
var ajax_request = new XMLHttpRequest();
ajax_request.open( "POST", ajax_url, true );
// Establecer la cabecera Content-Type apropiada
ajax_request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
// Enviar la solicitud
ajax_request.send( myjson );

Pero PHP seguirá esperando que el cuerpo de la solicitud sea un query string. Al recibir un cuerpo con otro formato, no podremos acceder desde PHP a esos datos en la superglobal $_POST como es habitual. Tendremos que acceder directamente al cuerpo de la solicitud y decodificar el JSON:

/// Obtenemos el json enviado
$data = file_get_contents('php://input');
// Los convertimos en un array
$data = json_decode( $data, true );

Por eso decía que los datos se debían enviar como query string, porque en PHP no tiene mucho “sentido” enviarlos en otros formatos. Sin embargo, puede ser apropiado si en el servidor se utiliza otro lenguaje. Por ejemplo, enviar JSON mediante POST puede ser apropiado para servidores que utilicen Node.js.

Recepción de la respuesta

Ya sabemos cómo enviar la solicitud al servidor. Para recibir la respuesta se utiliza la propiedad XMLHttpRequest.onreadystatechange para ejecutar una función cuándo XMLHttpRequest.readyState cambia de valor. El valor de readyState que más nos interesa es 4; este valor indica que la solicitud Ajax ha concluido y la respuesta desde el servidor está disponible en XMLHttpRequest.responseText:

// Definimos la URL que vamos a solicitar via Ajax
var ajax_url = "http://mysite.com/json/data.json";

// Definirmos los parámetros que vamos a enviar
var params = "parametro=valor&otro_parametro=otro_valor";

// Añadimos los parámetros a la URL
ajax_url += '?' + params;

// Creamos un nuevo objeto encargado de la comunicación
var ajax_request = new XMLHttpRequest();

// Definimos una función a ejecutar cuándo la solicitud Ajax tiene alguna información
ajax_request.onreadystatechange = function() {

    // readyState es 4
    if (ajax_request.readyState == 4 ) {

        // Analizaos el responseText que contendrá el JSON enviado desde el servidor
        var jsonObj = JSON.parse( ajax_request.responseText );
        // La variable jsonObj ahora contiene un objeto con los datos recibidos

    }
}

// Definimos como queremos realizar la comunicación
ajax_request.open( "GET", ajax_url, true );

//Enviamos la solicitud
ajax_request.send();

Trabajando con el formato JSON en JavaScript

Aunque el propio nombre Ajax signifique Asynchronous JavaScript And XML y el objeto utilizado para la comunicación cliente-servidor se llame XMLHttpRequest, con Ajax se puede solicitar en realidad cualquier tipo de datos: XML, HTML, texto plano, JavaScript, CSS y cualquier otro formato que se te pueda ocurrir, incluido el formato JSON que hoy en día es el formato de elección para intercambio de datos cuándo se trabaja con Ajax.

JSON son las siglas de JavaScript Object Notation (Notación de Objeto JavaScript). Los objetos JavaScript tienen una estructura compuesta por pares key : value (clave : valor):

{key : value, key2 : value2, key3 : value3, ...}

En el siguiente ejemplo la variable misDatos es un objeto JavaScript y tiene tres elementos cuyas key son nombre, apellidos y edad, y se puede acceder a sus valores con misDatos.name, misDatos.apellidos y misDatos.edad.

// La variable misDatos contiene un objeto JavaScript
var misDatos = {"nombre" : "Antonio", "apellidos" : "Molina Ballesteros", "edad" : 35};

console.log( misDatos.nombre );
console.log( misDatos.apellidos );
console.log( misDatos.edad );

Los objetos pueden ser multidimensionales; por ejemplo:

{
key : value,
key2 : {
        subkey  : subvalue,
        subkey2 : subvalue2
    }
}

Y se accede a los subkeys como jsonObj.key2.subkey, jsonObj.key2.subkey2, etc:

// La variable misDatos contiene un objeto JavaScript
var misDatos = {
    "nombre" : "Antonio",
    "apellidos" : "Molina Ballesteros",
    "edad" : 35,
    "direccion" : {
        "calle" : "Gran Via, 2",
        "ciudad" : "Madrid"
    }
};

console.log( misDatos.nombre );
console.log( misDatos.apellidos );
console.log( misDatos.edad );
console.log( misDatos.direccion.calle );
console.log( misDatos.direccion.ciudad );

JSON.parse

La respuesta JSON en una solicitud Ajax es un string, no es un objeto JavaScript sino una cadena de texto con notación de objeto JavaScript, esto es JSON. Es necesario convertir esta cadena a un objeto antes de intentar trabajar con los datos. Esta conversión se realiza con el método JSON.parse. El siguiente ejemplo es exactamente igual que el anterior pero la variable misDatos es un string (fíjate en las comillas) y es necesario aplicar JSON.parse para convertirlo en un objeto JavaScript:

// La variable misDatos es un string en notación JSON, pero no un objeto
var misDatos = '{ "nombre" : "Antonio", "apellidos" : "Molina Ballesteros", "edad" : 35, "direccion" : { "calle" : "Gran Via, 2", "ciudad" : "Madrid"} }';

// Se pasa el string a un objeto JSON
var jsonObj = JSON.parse( misDatos );

// Ahora, la variable jsonObj contiene el valor de cada key accesible en la forma jsonObj.key
console.log( jsonObj.nombre );
console.log( jsonObj.apellidos );
console.log( jsonObj.edad );
console.log( jsonObj.direccion.calle );
console.log( jsonObj.direccion.ciudad );

Ejemplo Ajax e interacción con PHP

Ya hemos visto como construir una solicitud Ajax y como trabajar con objetos en JavaScript de forma básica, así que estamos listos para combinar ambos. Vamos a ver un ejemplo en el que se envía una solicitud Ajax a un script PHP que devuelve información en formato JSON. Voy a utilizar el mismo ejemplo que en el tutorial mencionado al principio de este tutorial, tan sólo cambia el JavaScript utilizado, el PHP es exactamente igual y no lo voy a repetir aquí, aunque puedes descargarte el ejemplo completo más abajo.

<!DOCTYPE html>
<html lang="es-ES">
<head>
  <meta charset="UTF-8">
 
  <script>
 
    //getdeails será nuestra función para enviar la solicitud ajax
    var getdetails = function( id ){
      // Definimos la URL que vamos a solicitar via Ajax
      var ajax_url = "personas.php";

      // Definimos los parámetros que vamos a enviar
      // Debería trabajar en hacer esto un poco más limpio, de momento vale para hacer funcionar el ejemplo
      var params = '';
 
      id = JSON.parse(id).toString();

      id = id.split(",");
 
      if( id.length > 1 ) {
        //Si hay más de un id, enviar como query string array
        for( elem in id ) {
          params += "id[]=" + id[elem] +"&";
        }
      } else {
        params = "id=" + id;
      }
 
      //Añadimos los parámetros a la URL
      ajax_url += '?' + params;

      // Creamos un nuevo objeto encargado de la comunicación
      var ajax_request = new XMLHttpRequest();

      // Definimos una función a ejecutar cuándo la solicitud Ajax tiene alguna información
      ajax_request.onreadystatechange = function() {

        // see readyState es 4, proseguir
        if (ajax_request.readyState == 4 ) {

          // Analizaos el responseText que contendrá el JSON enviado desde el servidor
          var response = JSON.parse( ajax_request.responseText );
 
          if( response.success ) {
 
            var output = "<h1>" + response.data.message + "</h1>";
            //recorremos cada usuario
            for ( user in response.data.users ) {

              output += "<h2>Detalles del usuario " + response.data.users[user].ID + "</h2>";
 
              //recorremos los valores de cada usuario
              for ( userdata in response.data.users[user] ) {
 
                output += '<ul>';
                output += '<li>' + userdata + ': ' + response.data.users[user][userdata] + "</li>";
                output += '</ul>';
 
              }
 
            }
 
            //Actualizamos el HTML del elemento con id="#response-container"
            document.getElementById("response-container").innerHTML = output;

          } else {
 
            //response.success no es true
            document.getElementById("response-container").innerHTML = 'No ha habido suerte: ' + response.data.message;
 
          }

        }
      }
 
    // Definimos como queremos realizar la comunicación
    ajax_request.open( "GET", ajax_url, true );
 
    //Enviamos la solictud con los parámetros que habíamos definido
    ajax_request.send();

  };
 
   // Esperar a onload para poder acceder a los elementos del DOM
   window.onload = function() { 
 
     // Obtener todos los botones que utilizamos para lanzar la solicitud Ajax
     var userbutton = document.getElementsByTagName( "button" );

     for (var i=0; i < userbutton.length; i++) {
 
       // Para boton seleccionado, cuándo se haga click llamar a la funcion gedetails donde manjamos la solicitud ajax
       userbutton[i].onclick = function() {
 
         document.getElementById("response-container").innerHTML = "<p>Buscando...</p>";
 
         // Obtener el valor de data-user, pasar a array si se necesita y ejecutar la funcion getdetails()
         var id = this.getAttribute('data-user');

         getdetails( id );

       }
 
     }

  };
 
 </script>
</head>
<body>
 <p><button class="userdata" data-user="1">Dame los datos de la persona con ID = 1</button> - <button class="userdata" data-user='["1","2","3"]'>Dame los datos de las personas con ID = 1, ID = 2 e ID = 3.</button> - <button class="userdata" data-user="0">Ningún usuario</button></p>
 <div id="response-container"></div>
</body>
</html>

Nota: A continuación puedes descargarte el ejemplo completo, incluido el script PHP. Antes de intentar ver el ejemplo en vivo, revisa el fichero PHP y cambia las credenciales de la base de datos de ejemplo que has tenido que crear previamente.

Descargar el ejemplo completo

Referencias

  1. Objetos globales: JSON. Mozilla Developers Network.
  2. JavaScript Objets. w3schools.
  3. Web API Reference: XMLHttpRequest. Mozilla Developers Network.
  • Randy Suárez

    este me sirve para mandar los parámetros a un servidor externo?

    • Hola Randy,

      Si con servidor externo te refieres a otro dominio, Ajax no te vale tal cuál. El objeto JavaScript XMLHttpRequest con el que trabaja Ajax sólo se puede utilizar para solicitudes al mismo dominio desde el que se ejecuta. Es lo que se conoce como same-origin policy.

      No obstante, hay varias técnicas que permiten hacer Ajax o solicitudes asíncronas entre diferentes dominios, como son el trabajo con JSONP o el nuevo estándar Cross-Origin Resource Sharing, pero ambos requieren implementación en el servidor del otro dominio. Si controlas el otro servidor podrás implementarlo, sino dependes de lo que el otro servidor permita.

    • Randy Suárez

      Gracias. Revisare entonces como hacerlo con jsonp.

  • Antonio Briz Ena

    Hola, he desarrollado una sencilla web para monitorizar una draga (canvas….con varias imágenes), y se mueve el brazo perfectamente introduciendo valores a mano en el formulario. Pero mi idea es que funcione en tiempo real. Como html5 trabaja con websockets, no puedo acceder directamente por TCP-IP a 127.0.0.1:28002 por ejemplo, pues el software de navegación Hydropro utilizado me permite enviar en continuo como cadena de texto cada valor necesario (ej angulo del brado) a un puerto localhost diferente.
    Se que necesito AJAX pues no quiero que se recarge toda la página con cada valor nuevo, sólo que cambie el formulario y con ello el canvas.
    Como hago para capturar una cadena de texto plano en la url 127.0.0.1:28002???
    Si pongo esa url en un navegador va saliendo el texto plano en continuo, así que debe ser posible.
    Como hago el XMLrequest si no se trata de un servidor web sino de un puerto loopback????
    muchas gracias

    • La verdad que no se muy bien de lo que me hablas. Lo siento. No sé si es una consulta más apropiada para un informático que para un desarrollador web.

      Lo qué si te puedo decir es que XMLRequest no existe en JavaScript, es XMLHttpRequest. Cómo su nombre indica, es una solicitud HTTP. Y no hay problema alguno en realizar solicitudes HTTP a puertos loopback, pero en ese puerto debe haber inevitablemetne un servidor HTTP que comprenda y enrute la solicitud.

      Yo utilizo XAMPP para, principalmente, disponer de un servidor HTTP en local (Apache) y permite configurar los puertos en los que trabajar.

      No sé si te valdrá. Si no, puedes intentar instalar directamente Apache, el servidor HTTP open soruce más popular.

  • Miguel

    Hola que tal buenos dias. Mire yo tengo una pregunta. Tengo un formulario de inicio de sesion que quiero enviar a una hoja PHP mediante AJAX y en formato JSON. Hasta ahora logro recuperar los valores y formar un objeto javascript listo para enviarlo a la hoja PHP pero no si si json.stringify no lo codifica o el PHP no lo recibe. No estoy seguro que como hacer que php lo reciba. Me gustaria que haga un ejemplo que de como codificar JSON en base a un formulario de inicio de sesion y de como recibirlo en PHP. Gracias y muy buen post.

    • Miguel

      Formulo mi pregunta porque en el ejemplo estas enviando datos en formato XML y yo trato de enviarlo en formato JSON y mediante POST.

    • ¿Perdona? En ningún ejemplo se envían datos en formato XML. Si hay algún ejemplo que lo haga, por favor dime exactamente cuál para que lo corriga porque debe ser un error, pero yo no logro verlo ;).

  • Jesus Garcia Tena

    Buenas CybMeta,

    Mira estoy desarrollando una aplicación web y necesito enviar unos parametros que se seleccionas mediante dos select de un formulario a una direccion URL mediante POST en formato json. El caso es que los select son dependientes, en funcion de lo que elijas en el primero asi te salen distintas opciones en el segundo. ¿como puedo hacer para enviar estas variables?

    Muchas gracias.

    • ¿Enviar JSON con método POST desde JavaScript? ¿Seguro que quieres hacer eso? Técnicamente es posible pero no estoy muy seguro de que sea apropiado.

      Si tomas el código que ponía en el primer ejemplo con método POST, te bastaría con:

      1.- cambiar la variable params dónde se alamacena los datos a enviar. Pon aquí tu JSON

      2. cambiar el header de la solicitud a application/json

      El ejemplo quedaría asi:

      var ajax_url = "http://mysite.com/json/data.json";

      // Aquí construyes el objeto JSON
      var params = { "parametro" : "valor" , "otro_parametro" : "otro_valor" };

      var ajax_request = new XMLHttpRequest();

      ajax_request.open( "POST", ajax_url, true );

      // Ponemos las cabeceras de la solicitud adecuadas
      ajax_request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");

      ajax_request.send( params );

      Lo que pasa, como digo en el tutorial, que es necesario que se envíe como formulario ("Content-type", "application/x-www-form-urlencoded") al utilizar el método POST, de lo contrario en el servidor no llegará la información en la superglobal $_POST ni $_REQUEST, no estoy seguro como se podría solucionar eso, ni siquiera estoy seguro de que se pueda solucionar, tendría que investigarlo.

      De todas formas no le veo ninguna utilidad enviar JSON con POST desde Javascript. Si envías la info de forma “normal” al servidor, tendrás los datos enviados almacenados en $_POST y transformalo a JSON es tan fácil como:

      $mijson = json_enconde( $_POST );

      Así que, ¿hay algún motivo por el que quieras enviar los datos en JSON al servidor?

    • Jesus Garcia Tena

      Entonces solo basta con enviar el formulario directamente con post y con esa funcion se transofrma en json?? Dicha funcion la pongo en la parte del servidor?? disculpa que pregunte tanto pero es que soy muy novato en esto…

    • Lo que se hace con Ajax es, en este caso, muy similar a enviar el formulario directamente. La diferencia al hacerlo con Ajax es que se hace de forma asíncrona, es decir, con una comunicación entre el navegador y el servidor sin que se recargue toda la página de nuevo, lo que suele mejorar considerablemente la experiencia del usuario y reducir el consumo de recursos.

      Pero es completamente opcional.

      He estado investigando un poco y para leer en PHP una solicitud POST con JSON (Content-Type igual a application/json) se puede utilizar php://input para acceder al cuerpo de la solicitud en bruto. Pero de todas formas en el servidor tendrías que transformar el JSON en array (u en objeto PHP) si quieres trabajar con los datos:

      // Obtenemos el json enviado
      $data = file_get_contents('php://input');

      // Los convertimos en un array
      $data = json_decode( $data, true );

      Así que igualmente tienes que convertir el formato de datos. La verdad que sigo verle la utilidad de enviar JSON a través de POST para enviar un formulario.

    • Jesus Garcia Tena

      Entonces es mejor enviar los datos directamente con post y en el propio servidor los convierto con json??

      Por otro lado al ser un formulario dependiente no me creara problema al enviar las variables??

      Y muchisimas gracias por tu tiempo y tu ayuda de verdad!!

    • Como vengo diciendo, de forma general para mí no tiene sentido enviar JSON a PHP mediante método POST (entendiéndolo como Content-Type de la solicitud). Pero como no sé que es lo que pretendes hacer en el lado del servidor, no te puedo decir que es lo mejor. Hemos visto como técnicamente es posible enviar JSON a PHP y como se podría hacer utilizando Ajax. La decisión final ya es cosa tuya.

      Y problemas por tener fields dependientes unos de otros no tiene por qué darte; la dependencia entre fields es algo de la lógica de validación del formulario, nada que ver con su envío. Esto ya se está desviando del tema del tutorial.

    • Una entrada en StackOverflow relacionada con tu consulta y que me ha parecido muy interesante → http://stackoverflow.com/questions/12249358/posting-json-to-php-script

      Esa referencia, y el cada vez mayo uso de JavaScript también server-side, me da que pensar que puede que en el futuro, POST con JSON sea algo muy habitutal, también en PHP.

  • Franco

    Excelente explicación!