martes, 31 de marzo de 2009

Sobre el proyecto Mono



¡Saludos blogueros!

Este es un pequeño artículo hecho a partir de las anotaciones surgidas en una discusión con un amigo, sobre la posibilidad de migrar sus aplicaciones ASP. Net a un entorno FreeBSD.

El proyecto Mono consiste en una plataforma de software que nos permite ejecutar aplicaciones desarrolladas en .Net, sobre sistemas operativos diferentes a Windows de Microsoft, este proyecto es actualmente liderado por el desarrollador mexicano Miguel de Icaza. Una de las cosas que me agradaron al leer sobre este proyecto es que no solo nos ofrece una máquina virtual para ejecutar una aplicación compiladas en Visual Studio, (como hace la plataforma Java con sus archivos .class), sino que también cuenta con su propio compilador para programas desarrollados en diferentes lenguajes, como C# y Visual Basic, así como Phyton y Java.

Uno de los objetivos de este proyecto es hacer a las aplicaciones .Net verdaderamente multiplataforma, ya que, como la información lo indica, Mono funciona en GNU/Linux, FreeBSD, UNIX, Mac OS X, Solaris y plataformas Windows.

Para conseguir este último punto es que Mono utiliza el Common Language Runtime (CLR), ya que al compilar todos los programas escritos en los lenguajes que mencionamos se crea uno o más archivos en un código intermedio -similar al Bytecode de Java-, el cual sigue las especificaciones del CLR y puede ser interpretado por la máquina virtual con que cuenta Mono, entonces esta aplicación lleva a cabo la ejecución del programa, traduciendo las instrucciones en código intermedio al conjunto de instrucciones soportado por el sistema operativo sobre el cual está corriendo. Como se puede ver, es el mismo comportamiento del núcleo del framework de .NET de Microsoft, con la diferencia de que este solo puede encontrarse en entornos Windows.

Además Mono cuenta con un conjunto de bibliotecas de clases para obtener diferentes funcionalidades que facilitan el desarrollo de software, las cuales además pueden ser ejecutadas en cualquier otro entorno que cuente con un interprete para CLR.


Como indica el diagrama, nuestras aplicaciones escritas en alguno de los lenguajes mencionados (sí, yo también prefiero C#) utilizarán las bibliotecas de clases que nos ofrece la plataforma, las cuales están soportadas por el compilador y la máquina virtual de lenguaje común de infraestructura (CLI), que implementa la interpretación de programas en CLR y permite su ejecución en cualquiera de los sistemas operativos antes mencionados.

Podemos encontrar la versión actual (2.4) de Mono en su sitio oficial, y también en el sitio en español Mono Hispano.

Existen algunos entornos visuales open source para desarrollar aplicaciones .Net, entre las que encontramos SharpDevelop, el cual hasta ahora me ha parecido el más maduro de todos.

En el video que sigue a continuación, podemos encontrar parte de una conferencia que dio Miguel de Icaza hace un par de años en Badajoz España sobre el proyecto Mono, Dónde además nos enseña un ejemplo de como utilizar esta plataforma en GNU/Linux:



Desafortunadamente no he conseguido algo más reciente y la calidad no es lo que podríamos esperar pero aún así vale la pena verlo.

jueves, 19 de marzo de 2009

Eclipse Ganymede y el PDT 2.0


Y las lunas de Júpiter que faltan

Hace poco descargué e instalé la versión 3.4.1 de Eclipse, que incluye la versión 2.0 de PDT (PHP Development Tools), estos son los dos grandes componentes de este entorno de desarrollo, que como podemos leer en las características mencionadas tiene la gran ventaja de ser multiplataforma (ya que utiliza la máquina virtual de Java) y tratarse de un proyecto de software libre. Eclipse representa la parte medular de todo el paquete, sosteniendo un entorno que nos permite desarrollar aplicaciones en lenguaje Java, y a este módulo se le agregaron un conjunto de componentes (PDT) que le permitieron expandir sus capacidad, y ofrecernos apoyo en el desarrollo de aplicaciones Web en PHP y Javascript. Los que me agrada de este entorno (además de ser totalmente libre), el la asistencia que nos brinda en la programación, tanto en la captura de código como en la organización de nuestros proyectos, sobre todo en el manejo de clases y objetos permitiéndonos tener árboles de contenido de nuestras clases con sus tipos de métodos, propiedades, atributos, etc.:




Así como visualizar las características de los métodos y funciones, y tener listados de las clases y variables mientras estamos tecleando.

Ganymede es el nombre que se le dio a esta última versión de Eclipse, que también es el nombre de otra de las lunas de Júpiter, ya que la versión anterior se llamó Europa. Así como en toda nueva versión, se corrigieron errores, se agregaron funcionalidades y además esta promete ser una aplicación más rápida que sus predecesoras, cosa que hasta ahora ha cumplido.

En la página oficial, que encontramos en el segundo enlace que coloqué, podemos encontrar toda la información relacionada con el entorno, desde cómo instalarlo y actualizarlo hasta tutoriales que nos mostrarán cómo trabajar con él.

Otro sitio que puede ayudar a introducirnos en el desarrollo con Eclipse es la Comunidad en español de Eclipse IDE, para los que quieran las guías totalmente en español.

Espero que este pequeño artículo le sea de utilidad a más de uno.

Hasta la próxima.

miércoles, 18 de marzo de 2009

Consumiendo un Servicio Web en PHP desde AJAX, Parte 2: Crear el cliente

Después de una espera un poco larga ya estoy de vuelta para terminar con el tema del servicio Web consumido por AJAX, ahora trabajando del lado del cliente. Antes de comenzar es importante que revisen el tema anterior sobre la creación del servicio Web ya que hice un par de cambios en el código, para agregar un mejor manejo de errores en los métodos (del lado del cliente llamaremos métodos a las funciones PHP del servicio Web). Entonces, ahora nos enfocaremos en el cliente, ¿qué es lo que tendrá que hacer? pues acceder al servicio Web a través de una petición en formato XML, que seguirá el estándar del protocolo SOAP para peticiones, es decir el cliente envía una petición como la siguiente:
<?xml version='1.0' encoding='utf-8'?>
<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
<soap:Body>
<buscaAutor xmlns='http://localhost'>
<id xsi:type='xsd:string'>1</id>
</buscaAutor>
</soap:Body>
</soap:Envelope>


En donde la primer etiqueta soap:Envolepe representa el encabezado de una petición SOAP, en el que se establecen los formatos que se utilizarán en la petición, mientras que la etiqueta soap:Body representa el cuerpo de la petición, dentro de la cual se incluyen el nombre del método que se está accediendo (buscaAutor), el namespace del servicio Web (xmlns='http://localhost') y los parámetros que recibirá este método, representando cada parámetro con una etiqueta identificada con el nombre correspondiente. En el ejemplo anterior se envía el parámetro id que es de tipo string y posee el valor de 1.

La respuesta devuelta por el servicio Web tendrá un formato XML que también seguirá el estándar del protocolo SOAP, conteniendo en ella el resultado devuelto por el método al que se accedió, por ejemplo, al acceder al método buscaAutor con un id igual a 1 la respuesta generada es la siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:buscaAutorResponse xmlns:ns1="http://locahost">
<nombre xsi:type="xsd:string">Pablo Neruda</nombre>
<nacionalidad xsi:type="xsd:string">Chileno</nacionalidad>
<profesion xsi:type="xsd:string">Poeta</profesion>
<error xsi:type="xsd:string"></error>
</ns1:buscaAutorResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


En donde el servicio Web responde con un archivo XML que inicia con el encabezdo SOAP-ENV:Envelope para indicar los formatos que esta utilizando, seguido por la etiqueta SOAP-ENV:Body dentro de la cual encontramos el cuerpo de la respuesta, que contiene el resultado de la ejecución del método accedido indicada por la etiqueta ns1:buscaAutorResponse, en la que encontramos los valores devueltos por el resultado, cada uno siendo identificado por las etiquetas nombre, nacionalidad, profesión y error, en esta última podemos ver que no se generó algún mensaje de error al encontrarla vacía.

Entonces nuestro cliente AJAX consistirá en un programa que realizará una petición en formato XML, siguiendo las características que indica protocolo SOAP y que además deberá procesar la respuesta también en formato XML para extraer los datos devueltos y desplegarlos en la pantalla.

Programación del cliente

El cliente accederá al servicio Web ServicioDeAutores y podrá consumir cualquiera de sus dos métodos: buscaAutor y actualizaAutor, para lo cual el archivo HTML de la implementación contendrá un formulario que servirá para la doble tarea de capturar y desplegar los datos que serán enviados y recibidos respectivamente.

En esta ocasión volveré a utilizar la biblioteca Prototype que vimos en un post anterior, para facilitar el acceso a los contenidos de la página desde Javascript, sin embargo, no utilizare la funcionalidad para AJAX que ofrece, debido a que esta me arrojo algunos problemas para enviar los parámetros en formato XML, además tampoco me apoyare en otras bibliotecas para Javascript especializadas en el uso de servicios Web, porque no encontré alguna que fuera lo bastante estándar. En cambio, programaré toda la petición y procesamiento de la respuesta utilizando únicamente la clase XMLHttpRequest, dicho de otra forma lo haré todo a mano.

El código completo del archivo HTML para la implementación del cliente es la siguiente:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Petición a un servicio Web con Ajax, hecha a mano (como los verdaderos hombres)</title>
<script type="text/javascript" src="prototype-1.6.0.3.js"><!-- Referencia a Prototype--></script>
<script type="text/javascript">
var READY_STATE_COMPLETE=4;
var peticionHttp = null;
function inicializaXhr() { //Función que devuelve el objeto de la clase XMLHttpRequest
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
    }
    else if(window.ActiveXObject) {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

function peticionDeBusqueda() { //Función en la que se realiza la petición del método buscaAutor en el servicio Web
    var url = "http://localhost/servicio.php"; //URL del servicio Web
    var ns = "http://localhost"; //Namespace del servicio Web
    var arrParam = creaParamBusqueda(); //Obtenemos los parámetros de la busqueda
    var cadenaXML = creaCadenaXMLDePeticion("buscaAutor",ns,arrParam); //Creamos una cadena con formato XML que contendrá la información de la petición    
    peticionHttp = inicializaXhr();
    if(peticionHttp) {
        modificaBoton("btnBuscar","Buscando...",true); //Deahabilitamos el botón de busqueda
        peticionHttp.onreadystatechange = recibeArreglo; //Especificamos la función que obtendrá el arreglo de datos devuelto por el servicio Web
        peticionHttp.open("POST", url, true); //Específicamos el metódo, url y tipo de la petición
        peticionHttp.setRequestHeader ("SOAPAction", "http://localhost/servicio.php/buscaAutor");  //Agregamos al encabezado de la petición el indicador del protocolo SOAP
        peticionHttp.setRequestHeader ("Content-Type", "text/xml");  //Agregamos al encabezado de la petición que el contenido se trata de un documento XML
        peticionHttp.send(cadenaXML); //Enviamos los parámetros
    }
    $('divRespuesta').innerHTML = ""; //*Nota: El símbolo $ es equivalente a document.getElementById
}

function peticionDeActualizacion() { //Función en la que se realiza la petición del método actualizaAutor en el servicio Web
    var url = "http://localhost/servicio.php"; //URL del servicio Web
    var ns = "http://localhost"; //Namespace del servicio Web
    var arrParam = creaParamEnvio(); //Obtenemos los parámetros del envio de datos para la actualización
    var cadenaXML = creaCadenaXMLDePeticion("actualizaAutor",ns,arrParam); //Creamos el objeto XML que contendrá la información de la petición    
    peticionHttp = inicializaXhr();
    if(peticionHttp) {
        modificaBoton("btnGuardar","Guardando...",true);
        peticionHttp.onreadystatechange = recibeMensaje;
        peticionHttp.open("POST", url, true);
        peticionHttp.setRequestHeader ("SOAPAction", "http://localhost/servicio.php/actualizaAutor");
        peticionHttp.setRequestHeader ("Content-Type", "text/xml");
        peticionHttp.send(cadenaXML);
    }
}

function recibeArreglo() { //Función que manejara la respuesta recibida por el servidor al solicitar el método buscaAutor
    if(peticionHttp.readyState == READY_STATE_COMPLETE) {
        if(peticionHttp.status == 200) {
            var documentoXML = peticionHttp.responseXML;            
            var objResp = {nombre: "", nacionalidad: "", profesion:""}; //Las propiedades identifican el nombre del campo que deseamos extraer del documento
            procesaXML(documentoXML, "buscaAutor", objResp);
            if(!objResp.error) //Verificamos si se obtuvo algún error de la respuesta
            {
                $('txtNombre').value = objResp.nombre;
                $('txtNacionalidad').value = objResp.nacionalidad;
                $('txtProfesion').value = objResp.profesion;
            }
            else
                alert(objResp.error);
            modificaBoton("btnBuscar","Buscar",false);
        }
    }
}

function recibeMensaje() {//Función que manejara la respuesta recibida por el servidor al solicitar el método buscaAutor
    if(peticionHttp.readyState == READY_STATE_COMPLETE) {
        if(peticionHttp.status == 200) {
            var documentoXML = peticionHttp.responseXML;
            var objResp = {mensaje: ""}; //Las propiedades identifican el nombre del campo que deseamos extraer del documento
            procesaXML(documentoXML, "actualizaAutor", objResp);
            if(objResp.mensaje.indexOf("Error") < 0) //Verificamos si la cadena contiene algún error
                $('divRespuesta').innerHTML = objResp.mensaje;
            else
                alert(objResp.mensaje);
            modificaBoton("btnGuardar","Guardar",false);
        }
    }
}

function procesaXML(documXML, nomMetodo, objSalida) { //Función que extre del objeto ducumXML los valores devueltos por el Servicio Web dentro del objeto objSalida
    var nomRespuesta = "ns1:" + nomMetodo + "Response";
    var raiz = documXML.getElementsByTagName('SOAP-ENV:Envelope')[0];
    var cuerpo = raiz.getElementsByTagName('SOAP-ENV:Body')[0];
    if(!cuerpo.getElementsByTagName('SOAP-ENV:Fault')[0])
    {   
        var respuesta = cuerpo.getElementsByTagName(nomRespuesta)[0];
        for(var campo in objSalida)
        {
            if(respuesta.getElementsByTagName(campo)[0].firstChild)
                objSalida[campo] = respuesta.getElementsByTagName(campo)[0].firstChild.nodeValue;
        }
    if(respuesta.getElementsByTagName('error')[0] && respuesta.getElementsByTagName('error')[0].firstChild)
        objSalida.error = respuesta.getElementsByTagName('error')[0].firstChild.nodeValue;
    }
    else {
        var respuesta = cuerpo.getElementsByTagName('SOAP-ENV:Fault')[0];
        objSalida.error = respuesta.getElementsByTagName('faultstring')[0].firstChild.nodeValue;
    }
}

function creaCadenaXMLDePeticion(nomMetodo, ns, arrParametros) { //Devuelve una cadena que representa al documento XML que será enviado como petición al Servicio Web
    var cadenaXML = "<?xml version='1.0' encoding='utf-8'?>";
    cadenaXML += "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' ";
    cadenaXML += "xmlns:xsd='http://www.w3.org/2001/XMLSchema' ";
    cadenaXML += "xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>";
    cadenaXML += "<soap:Body>"
    cadenaXML += "<" + nomMetodo + " xmlns='"+ ns +"'>"; //Elemento XML en el que aparece el nombre del método solicitado y el namespace especificado
    tope = arrParametros.length;
    for(var i=0; i < tope; i++) { //Para cada parámetro que requiere el método se crea un elemento con la información del objeto del arreglo correspondiente
        cadenaXML += "<"+arrParametros[i].nombre+" xsi:type='xsd:"+arrParametros[i].tipo+"'>"+arrParametros[i].valor+"</"+arrParametros[i].nombre+">";
    }
    cadenaXML += "</"+nomMetodo+">";
    cadenaXML += "</soap:Body></soap:Envelope>";
    return cadenaXML;
}

function creaParamBusqueda() { //Función que crea un arreglo que contiene el objeto del parámetro para la busqueda
    var arreglo = new Array(
    {nombre:'id', valor: $("txtId").value, tipo: "string"}
    ); //El objeto representa el parametro id de la función buscaAutor
    return arreglo; //*Nota: El símbolo $ es equivalente a document.getElementById, esta es una funcionalidad que nos ofrece la biblioteca Prototype
}

function creaParamEnvio() {//Función que crea un arreglo que contiene los objetos de los parámetros para la actualización
    var arreglo = new Array(
    {nombre:'id', valor: $("txtId").value, tipo: "string"},
    {nombre:'nombre', valor: $("txtNombre").value, tipo: "string"},
    {nombre:'nacionalidad', valor: $("txtNacionalidad").value, tipo: "string"},
    {nombre:'profesion', valor: $("txtProfesion").value, tipo: "string"}
    ); //Cada uno de los objetos que se encuentran en el arreglo representan los parámetros de la función del servicio Web (id, nombre, nacionalidad, profesion)
    return arreglo; //*Nota: El símbolo $ es equivalente a document.getElementById, esta es una funcionalidad que nos ofrece la biblioteca Prototype
}

function modificaBoton(id, valor, deshabilitar) { //Función que puede habilitar o deshabilitar de un elemento especificado, así como cambiar su valor
    if($(id)) {
        if(valor !="")
            $(id).value = valor;
        $(id).disabled = deshabilitar;        
    }
}
</script>
</head>
<body>
<form>
<p><label>Id:</label> <input type="text" name="txtId" id="txtId" size="5" /> <input type="button" value="Buscar" id="btnBuscar" onclick="peticionDeBusqueda()" /></p>
<p>
  <label>Nombre:</label>
  <input type="text" id="txtNombre" name="txtNombre" size="40" /><br/>
  <label>Nacionalidad:</label>
  <input type="text" id="txtNacionalidad" name="txtNacionalidad" size="30" /><br/>
  <label>Profesion:</label>
  <input type="text" id="txtProfesion" name="txtProfesion" size="50" /><br/>
  <input type="button" value="Guardar" id="btnGuardar" onclick="peticionDeActualizacion()" /></p>
  </form>
<div id="divRespuesta"></div>
</body>
</html>


Como pueden ver, es un código algo largo y es que incluí algunas funcionalidades adicionales a lo que habíamos venido manejando hasta ahora. Una vez más recurrimos a la función inicializaXhr() para acceder a la instancia de la clase XMLHttpRequest, y este objeto es utilizado para iniciar dos peticiones dentro de las funciones peticionDeBusqueda y peticionDeActualizacion.

function peticionDeBusqueda() {
  var url = "http://localhost/servicio.php";
  var ns = "http://localhost";
  var arrParam = creaParamBusqueda();
  var cadenaXML = creaCadenaXMLDePeticion("buscaAutor",ns,arrParam);
  peticionHttp = inicializaXhr();
  if(peticionHttp) {
...


La función peticionDeBusqueda() será llamada al hacer clic sobre el botón btnBuscar, y al hacerlo se iniciará la petición a nuestro servicio Web del método buscaAutor. Como vemos en el código se define la URL del serivico Web, se declara el namespace de este servicio (ns) y después por medio de la función creaParamBusqueda, accedemos al valor del campo id capturado por el usuario en el formulario, el cual es alamacenado en un arreglo de objetos llamado arrParam, este arreglo es pasado como parámetro a la función creaCadenaXMLPeticion(), la cual utiliza éste y otros dos parámetros para crear una cadena con formato XML que representará el mensaje XML de la petición del método buscaAutor (siguiendo el formato que definimos al inicio de este post). En seguida se crea la instancia de la clase XMLHttpRequest y después de validarla se procede a realizar la petición:

modificaBoton("btnBuscar","Buscando...",true);
peticionHttp.onreadystatechange = recibeArreglo;
peticionHttp.open("POST", url, true);
peticionHttp.setRequestHeader ("SOAPAction", "http://localhost/servicio.php/buscaAutor");
peticionHttp.setRequestHeader ("Content-Type", "text/xml")
peticionHttp.send(cadenaXML);


Como vemos, se realiza una llamada a la función modificaBoton, cuya única función es deshabilitar el botón del formulario btnBuscar, para evitar que se pulse otra vez mientras se atiende la petición del servicio Web. En seguida se hace referencia a la función recibeArreglo() para indicar que se ejecute en cuanto se complete la solicitud al servidor Web, después con el método open se establecen las características de la petición al servidor Web. El método setRequestHeader se utiliza para agregar dos encabezados adicionales dentro de la petición, el primero para indicar que se trata de una petición que implementa el protocolo SOAP por medio del identificador "SOAPAction", y el segundo para indicar que el contenido de la petición tendrá un formato XML por medio del identificador "Content-Type" de tipo "text/xml". De esta forma, el texto que contiene cadenaXML será interpretado como el contenido XML de la petición enviada al servidor Web al momento de pasárselo a la al métod send, con lo cual se inicia la petición del método buscaAutor al servicio Web ServicioDeAutores.

La función peticionDeActualizacion() se iniciará haciendo clic sobre el botón identificado como btnGuardar, y su funcionamiento es muy parecido al de peticionDeBusqueda, con la diferencia de que aquí el método que se solicita es actualizaAutor y al cual se le proporcionan los datos del autor capturados en el formulario, a los cuales se accede por medio de la función creaParamEnvio():
var arreglo = new Array(
    {nombre:'id', valor: $("txtId").value, tipo: "string"},
    {nombre:'nombre', valor: $("txtNombre").value, tipo: "string"},
    {nombre:'nacionalidad', valor: $("txtNacionalidad").value, tipo: "string"},
    {nombre:'profesion', valor: $("txtProfesion").value, tipo: "string"}
    );
    return arreglo;


Que como vemos simplemente crea un arreglo que contiene los valores capturados en este formulario, dentro de una serie de objetos (delimitados con la apertura y cierre de llaves "{}"), en los que además del valor se establece el nombre y tipo de dato para los valores recogidos, debido a que este arreglo se utilizará en la función creaCadenaXMLDePeticion para elaborar el texto del contenido XML para la petición, en donde los valores de este arreglo se usarán para establecer los parámetros del método que se solicita:

var cadenaXML = "<?xml version='1.0' encoding='utf-8'?>";
cadenaXML += "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' ";
cadenaXML += "xmlns:xsd='http://www.w3.org/2001/XMLSchema' ";
cadenaXML += "xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>";
cadenaXML += "<soap:Body>"
cadenaXML += "<" + nomMetodo + " xmlns='"+ ns +"'>";
tope = arrParametros.length;
for(var i=0; i < tope; i++) {
    cadenaXML += "<"+arrParametros[i].nombre+" xsi:type='xsd:"+arrParametros[i].tipo+"'>"+arrParametros[i].valor+"</"+arrParametros[i].nombre+">";
}
cadenaXML += "</"+nomMetodo+">";
cadenaXML += "</soap:Body></soap:Envelope>";
return cadenaXML;


En la función creaCadenaXMLDePeticion la variable cadenaXML es utilizada para guardar el cuerpo de la petición XML que se va generando dentro, utilizando el nombre del método, nombre de namespace y el arreglo de valores que recibe como parámetros.

Una vez que se recibe la respuesta del servidor Web, en el caso del método actualizaAutor se ejecuta la función recibeMensaje, la cual después de validar el estado de la respuesta se encarga de procesar su contenido:
var documentoXML = peticionHttp.responseXML;
var objResp = {mensaje: ""};
procesaXML(documentoXML, "actualizaAutor", objResp);
if(objResp.mensaje.indexOf("Error") < 0)
    $('divRespuesta').innerHTML = objResp.mensaje;
else
    alert(objResp.mensaje);
modificaBoton("btnGuardar","Guardar",false);


Creando un objeto XML llamado documentoXML que representará el documento XML devuelto por el servicio Web, y podremos utilizarlo para extraer de él la información que nos interesa. En este caso queremos conocer el contenido del campo mensaje que el servicio devuelve como resultado de la ejecución del método actualizaAutor. Para lograrlo utilizamos la función procesaXML que recorre todo el documento XML y almacena dentro de una propiedad del objeto objResp el valor del campo que necesitamos. Una vez que tenemos a la mano este valor, podemos desplegar el mensaje devuelto por el servicio con la instrucción:
$('divRespuesta').innerHTML = objResp.mensaje;


Donde el simbolo '$' equivale a document.getElementById, la cual es una funcionalidad que nos ofrece Prototype para hacer nuestra captura más rápida.

Almacenando este archivo HTML podemos pasar a probar su funcionamiento, teniendo presenta cual es la URL del servicio Web que accederemos, por si se necesitan hacer cambios las url contenidas en el código Javascript.

Nota: Una vez más les recuerdo que antes de probar el servicio Web modifiquen el código PHP agregando la contraseña de acceso a su SMBD MySQL en la parte indicada por [TuContraseña].

Al acceder al cliente por medio de nuestro navegador Web, este debe tener una apariencia como la que sigue:


Podemos probar el servicio de búsqueda de autores capturando un identificador como sigue:


Si capturamos un identificador que no exista en la tabla de la base de datos el sistema nos responde los siguiente:


Si capturamos cambios en la información de un autor consultado, podemos probar el método para la actualización de la información:


O, si capturamos un nuevo identificador y hacemos clic en guardar el sistema interpretará que se trata de un nuevo registro realizando una inserción en la tabla:


Y de esta forma es que este pequeño sistema funciona, es obvio que le hacen falta diferentes validaciones, pero cumple con el propósito que nos fijamos al inicio al consumir exitosamente los métodos del servicio Web desarrollado.

Conclusiones

Este fue el último post dedicado al aprendizaje de AJAX dentro de este blog, con todas las anotaciones hechas hasta aquí considero que cubrí todas las expectativas que tenía al iniciar comenzar a aprender sobre este componente de Javascript, lo cual no significa que ya no dedicaré su tiempo a los temas relacionados con AJAX, sino que ya no me centraré en asuntos de nivel básico al momento de referirme estos, y que ahora exploraré otras herramientas de software para seguir haciendo pequeñas guías como esta. Si algún cibernauta se topa con este blog buscando un guía sobre el lenguaje AJAX espero que todos estos artículos le sean de utilidad, para cualquier duda o critica que quieran hacer esta la opción para dejar sus comentarios, los cuales siempre se agradecen.

Hasta la próxima.

martes, 10 de marzo de 2009

Consumiendo un Servicio Web en PHP desde AJAX, Parte 1: Crear el servicio


Otra solución para múltiples plataformas


Otra forma de que tenemos para acceder a la funcionalidad de nuestro servidor Web es a través de los Servicios Web, cuyo funcionamiento es similar al modo de trabajo que hemos utilizado hasta ahora en el que se solicita una página Web por medio de una petición HTTP, mandándole una serie de parámetros y recibiendo e interpretando la respuesta del servidor. Ahora bien, como la información nos indica los Servicios Web consisten una serie de estándares y protocolos para llevar a cabo el intercambio de datos entre nuestros clientes y el servidor Web, y es gracias a ésto que existe una diferencia entre ambos modos de trabajo, ya que gracias a estos protocolos trabajaremos de manera más transparente al realizar la comunicación entre nuestras aplicaciones, al dejar varios de los detalles de esta labor a la implementación del servicio Web que estemos utilizando. El gran potencial de los servicios Web es que permiten la comunicación de aplicaciones situadas en diferentes plataformas de manera estándar, por lo que podríamos tener un servicio Web hecho en .Net y consumirlo desde un cliente Java, o un servicio Web en PHP y consumirlo desde un cliente .Net, con total independencia del sistema operativo o el manejador de base de datos que estemos utilizando.

En este artículo, me propongo mostrar el ejemplo del uso de un Servicio Web programado en PHP desde nuestro cliente AJAX, realizando las operaciones de consulta, inserción y actualización de los registros de una tabla en nuestra base de datos obras_literarias de MySQL, las cuales serán solicitadas desde nuestro cliente AJAX.

Antes de comenzar

Para desarrollar nuestro servicio Web desde PHP utilizaremos las implementaciones de los protocolos XML-RPC/SOAP que nos ofrece la clase soap_server, la cual esta contenido en la biblioteca del archivo nusoap.php que podemos descargar de manera totalmente gratuita desde el enlace que acabamos de indicar -la documentación completa la encuentran aquí-, en mi caso la última versión disponible que encontré es la 0.7.3. Una vez a nuestra disposición incluiremos el archivo nusoap.php en el directorio de nuestro servidor Web para hacerle referencia en el programa PHP que realizaremos. Además nos apoyaremos en la clase AccesoADatos que definimos en un post anterior.

Nomenclatura utilizada:

-El protocolo de comunicación que se utilizará esSOAP, que es el que define el mecanismo de comunicación entre un cliente (en esta caso en AJAX) y un servicio Web (en este caso en PHP).
-En la definición de este servicio Web también aparecerá el término WSDL siglas de Web Services Description Language, que es un formato XML que se utiliza de manera estándar para describir las características de los servicios Web. Dentro del código definiremos algunas de las características del WSDL del servicio Web que implementaremos.

El trabajo con la base de datos

El sistema manejador de base de datos seguirá siendo MySQL, dentro del cual tendremos la base de datos obras_literarias, en la cual encontramos la tabla autores, que ya habíamos utilizado en los posts anteriores y se puede crear a través del siguiente script:


CREATE TABLE `autores` (
`Id` mediumint(9) NOT NULL,
`Nombre` varchar(60) NOT NULL,
`Nacionalidad` varchar(50) NOT NULL,
`Profesion` varchar(60) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Si lo deseamos podemos insertar algunos registros, aunque no es un requisito ya que el sistema que haremos incluirá la inserción de registros:
INSERT INTO `autores` ( `Id` , `Nombre` , `Nacionalidad` , `Profesion` )
VALUES (
'1', 'Juan Rulfo', 'Mexicano', 'Novelista'
),(
'2', 'Pablo Neruda', 'Chileno', 'Poeta'
),(
'3', 'Miguel de Cervantes Saavedra', 'Español', 'Novelista, poeta y dramaturgo'
),(
'4', 'Jorge Luis Borges', 'Argentino', 'Ensayista, escritor y poeta'
);


Trabajo del lado del servidor

Para el servidor crearemos el programa PHP servicio.php, dentro del cual declararemos el servicio Web SOAP que que contendrá los métodos que realizarán las labores de consulta y actualización de la tabla autores.

El código para el trabajo es el siguiente (en seguida explicaré cada uno de los pasos que se fueron realizando):

servicio.php

<?php
require("nusoap.php"); //Referencia al archivo que contiene la clase soap_server
require("AccesoADatos.php");//Referencia al archivo que contiene la clase AccesoADatos
//Implementación del servicio Web
$servicio = new soap_server; //Instancia de la clase para la creación del servicio Web
$servicio->soap_defencoding = "UTF-8"; //Señalamos que el juego de caracteres que utilizaremos es UTF-8
/*
* Definimos un espacio de nombres (namespace) para el servicio Web, funciona como un identificador del servicio Web
* Web -en este caso usamos una URL-
*/
$ns = "http://localhost";
$servicio->configureWSDL('ServicioDeAutores',$ns); //Configuramos el WSDL del servicio Web (Param 1: Nombre del servicio Web, Param 2: Namespace del servicio Web)

/*
*Definimos cuales serán las funciones que poseerá nuestro servicio Web con el metodo register, como primer
* parámetro recibe el nombre de la función, en el segundo y tercer parámetro se incluyen dos arreglos asociativos que especifican los parámetros
* que reciben y los valores devueltos por estas funciones respectivamente
*/
$servicio->register('buscaAutor',
                    array('id'=>'xsd:string'),
                    array('nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string','error'=>'xsd:string'),
                    $ns); //Recibirá el id del autor que es de tipo string y devolverá un arreglo con los datos del registro
$servicio->register('actualizaAutor',
                    array('id'=>'xsd:string','nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string'),
                    array('mensaje'=>'xsd:string'),
                    $ns); //Recibirá los datos del autor y devolverá un mensaje tipo string
//Obtenemos los parámetros del encabezado HTTP
if(isset($HTTP_RAW_POST_DATA))
{
    $input = $HTTP_RAW_POST_DATA;
}
else
{
    $input = implode("\r\n",file("php://input")); //Secuencia de entrada php://input
}
$servicio->service($input); //Con este método que recibe el contenido del encabezado HTTP se procesa la petición para el servicio Web y se genera la respuesta
exit;

//Funciones para el servicio web
function buscaAutor($id) { //Función para la busqueda de autores
    $acceso = new AccesoADatos('localhost','root','[TuContraseña]','obras_literarias');
    $autor = array('nombre'=>"",'nacionalidad'=>"",'profesion'=>"");
    if($acceso->abreConexion()) { //Accedemos a la base de datos
        $consulta = "SELECT * FROM autores WHERE Id = $id LIMIT 1";
        if($acceso->consulta($consulta)) {//Realizamos la consulta y comprobamos el resultado
            if($acceso->devuelveRegsLeidos() > 0){//Comprobamos si se encontró el registro                                
                $renglon = $acceso->devuelveArreglo();//Extrae el renglon del resultado como un arreglo asosciativo
                $autor['nombre'] = $renglon['Nombre'];
                $autor['nacionalidad'] = $renglon['Nacionalidad'];
                $autor['profesion'] = $renglon['Profesion'];
            }
            else
                $autor['error'] = "Error: No se localizó un autor con el identificador específicado";
        }
        else
            $autor['error'] = "Error de acceso a datos";
    }
    else
        $autor['error'] = "Error de acceso a datos";
    return $autor;
}

function actualizaAutor($id, $nombre, $nacionalidad, $profesion) { //Función para la actualización de la tabla autores
    $acceso = new AccesoADatos('localhost','root','[TuContraseña]','obras_literarias');
    if($acceso->abreConexion()) { //Accedemos a la base de datos     
        $datos = array('Nombre'=>$nombre,'Nacionalidad'=>$nacionalidad,'Profesion'=>$profesion);
        $consulta = "SELECT * FROM autores WHERE Id = $id LIMIT 1";
        if($acceso->consulta($consulta)) { //Realizamos la consulta y comprobamos el resultado
            if($acceso->devuelveRegsLeidos() > 0) { //Comprobamos si se encotró el registro
                $campoLlave = array('Id'=>$id); //Si se encontró, significa que se está actualizando un registro existente                              
                if($acceso->actualizaRegistro("autores",$datos,$campoLlave) > 0)//Realizamos la actualización del registro                            
                    $mensaje = "El registro fué actualizado exitosamente";            
                else
                    $mensaje = "Error: No se registraron cambios en la información del autor";                                        
            }
            else { //En caso contrario se está realizando una insersión
                $datos['Id'] = $id;
                if($acceso->insertaRegistro("autores",$datos) > 0)//Realizamos la inserción del registro
                    $mensaje = "El registro fué insertado exitosamente";            
                else
                    $mensaje = "Error: No se completo la inserción del registro";
            }
        }
        else
            $mensaje = "Error de acceso a datos";            
    }
    else
        $mensaje = "Error de acceso a datos";     
    return $mensaje;
}
?>


Como vemos, al inicio se incluye la referencia a los archivos de las clases que utilizaremos:

require("nusoap.php");
require("AccesoADatos.php");

para implementar el servicio Web y para acceder a la base de datos respectivamente.

En seguida se crea la instancia de la clase soap_server con la cual implementaremos el servicio Web:

$servicio = new soap_server;


Específicamos que el juego de caracteres utilizado es UTF-8:

$servicio->soap_defencoding = "UTF-8";


Por medio de la variable $ns definimos el namespace del servicio Web, que representa el identificador único de nuestro servicio, este caso representado por medio de la URL de nuestro servidor Web local, que indica el directorio en el que podemos localizar el archivo del servicio Web por medio de HTTP:

$ns = "http://localhost";


También podríamos utilizar una URL con dirección IP, o una dirección con un nombre dominio propio servidor si disponemos de una.

En seguida definimos el WSDL del servicio Web, estableciendo el nombre del servicio Web como "ServicioDeAutores" y su namespace como "http://locahost/PruebasPHP":

$servicio->configureWSDL('ServicioDeAutores',$ns);


Una vez hecho esto a través del método registrer() especificamos cuales serán las funciones que ofrecerá ServicioDeAutores:

$servicio->register('buscaAutor',
                    array('id'=>'xsd:string'),
                    array('nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string','error'=>'xsd:string'),
                    $ns);
$servicio->register('actualizaAutor',
                    array('id'=>'xsd:string','nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string'),
                    array('mensaje'=>'xsd:string'),
                    $ns);

En el primer parámetro de este método definimos el nombre de la función mencionada, en el segundo se definen los parámetros de esta función, el tercer parámetro define los valores que serán devueltos por la función, pudiendo tratarse de un único valor o de un arreglo asociativo, dependiendo del número de valores que se definan. Cómo último parámetro el método register() recibe el namespace del servicio Web. La declaración de estas funciones aparecerá en el bloque final del código del archivo.

En seguida se obtiene el contenido completo del encabezado HTTP que recibe el programa PHP:

if(isset($HTTP_RAW_POST_DATA))
{
    $input = $HTTP_RAW_POST_DATA;
}
else
{
    $input = implode("\r\n",file("php://input"));
}

Este encabezado se puede ser accedido por medio de la variable predifinida $HTTP_RAW_POST_DATA o utilizando la secuencia de entrada php://input.

A continuación y para finalizar con la implementación del servicio Web utilizamos el método service(), el recibe el contenido del encabezado HTTP y procesa la petición para el servicio Web y se genera la respuesta XML:

$servicio->service($input);
exit;


Seguido de la instrucción exit para finalizar el programa PHP.

En el siguiente bloque de código aparece la declaración de las funciones buscaAutor y actualizaAutor, en la primera se lleva a cabo la búsqueda de una autor en la base de datos por medio del $id recibido. La segunda realiza la actualización o inserción de un registro, dependiendo de si el valor campo "Id" proporcionado existe o no en la base de datos.

Nota: Antes de probar el servicio Web recuerden modificar el código anterior agregando la contraseña de acceso a su SMBD MySQL en la parte indicada por [TuContraseña].

Y ahora, gracias a este este código podremos implementar nuestro servicio Web que será consumido por el cliente AJAX que desarrollaremos, con la ventaja de que este mismo servicio puede ser utilizado por cualquier otra aplicación que soporte la utilización de servicios Web.

Para probar que todo anda bien con el ServicioDeAutores podemos acceder a él por medio de nuestro navegador, utilizando la ruta del servidor Web en la que está almacenado, para este ejemplo ésta sería: http://localhost/servicio.php, obteniendo un resultado como el que sigue:



Debido a que nuestro navegador es capas de interpretar el contenido XML de la respuesta del servicio Web, nos ofrece la posibilidad de visualizar su WSDL, así como las características de la funciones que éste posee. El WSDL arrojado en este ejemplo es el siguiente:

img


Mientras que al seleccionar alguna de las funciones nos ofrece una descripción como la que sigue:



Si todo salió bien nuestro servicio Web ya funciona, ahora todo lo que queda es probarlo desde un cliente, cuya programación veremos en el siguiente artículo.

Hasta la próxima.

martes, 3 de marzo de 2009

Un framework libre para trabajar con Ajax: Prototype


El framework de Ajax más popular: Prototype

¡Saludos nuevamente blogueros!

Básicamente un framework consiste en un conjunto de utilidades y bibliotecas que nos facilitan la realización de un desarrollo de software. Uno de los puntos importantes de los frameworks es que dentro de nuestra programación varios bloques de códigos son repetitivos, por lo que es posible contar un una herramienta que automatice la generación de este código para facilitar la construcción del software. Una vez un amigo me hizo una analogía muy buena de como debíamos ver lo que es un framework y para qué nos sirve, este puede verse como el armazón de una casa de madera, en la que el armazón representa las piezas de madera sobre las que se monta toda la estructura de las paredes, los pisos y los techos. Así, este conjunto de piezas de madera sustenta toda la construcción, pero al mismo tiempo, pueden utilizarse en otras construcciones con características similares, solo hay que tener la suficiente cantidad de piezas cortadas y listas para utilizarse nuevamente, por lo que ya no nos preocuparemos por tener que fabricar nosotros mismos un nuevo armazón. Entonces, regresando a la programación, el framework representará todas esas herramientas diseñadas previamente que nos facilitarán el trabajo de desarrollar nuevos sistemas de software.

Si recordamos los temas de Javascript y Ajax, encontramos que en los diferentes ejemplos hemos estado repitiendo varias de las rutinas para acceder a los recursos del servidor, entonces en este post veremos uno de los frameworks libres que existen para el trabajo con Javascript y Ajax, llamado Prototype, el cual fue diseñado originalmente por Sam Stephenson, pero que sus ultimas versiones incorpora código de otros programadores. Consiste en una biblioteca de objetos que encapsulan varias funcionalidades para Javascript y Ajax, dentro de los métodos y propiedades que contienen, tratando de simular una estructura de clases como la de otros lenguajes de programación.

Para probarlo, basta con acceder al sitio Web oficial para su descarga: http://www.prototypejs.org/

En la sección Download, encontraremos la última versión estable de esta biblioteca, contenida en un archivo *.js. La última versión estable que yo encontré corresponde al archivo prototype-1.6.0.3.js. Descargaremos este archivo en nuestro equipo y lo guardaremos en el directorio del servidor Web que contenga nuestros archivos HTML.

Para dar un ejemplo rápido de la utilización de esta biblioteca, realizaremos la programación de un nuevo cliente Ajax dentro de una página HTML, en la que incluiremos la referencia al archivo *.js que acabamos de descargar.

Consideraciones previas

Hay tres maneras de realizar una petición Ajax con Prototype:

1) Ajax.Request
2) Ajax.PeriodicalUpdater
3) Ajax.Updater

Para el ejemplo utilizaremos la funcionalidad Request del objeto Ajax llamada Ajax.Request, la cual nos permite realizar una petición de un recurso al servidor Web. Este método del objeto Ajax se instancía como si se tratara de una clase y recibe una serie de parámetros:

url
URL del recurso Web que será solicitado al servidor Web en la petición HTTP.


method
Este puede ser HTTP GET o HTTP POST. Por defecto está definido como POST.


parameters
Los parámetros representan las parejas llave-valor que serán enviados a la pagina solicitada, que representan los mismos datos que pasamos en la cadena de consulta (query) cuando solicitamos una URL remota en los ejemplos anteriores.



Eventos Prototype de Ajax:

Además de estos parámetros, para la petición dentro de Ajax.Request también se incluye una referencia a una serie de funciones que definen un grupo específico de eventos, que se ejecutan automáticamente cuando se completa una tarea o cuando ocurre un error:


onCreate:
Este evento es llamado siempre que se realiza una nueva petición Ajax con Ajax.Request. Es útil si deseamos ver una señal de que se ha iniciado una peticón Ajax.


onSuccess:
Este evento es llamado siempre que una petición es completada y el código de estado regresado esta dentro de 2xx.


onFailure:
Este evento es llamado siempre que la petición es completada y el código de estado no esta dentro de 2xx.



Manos a la obra



Para este ejemplo nos basaremos en el código del archivo HTML del tercer ejercicio que vimos con Ajax, que titulamos Combinando Ajax con PHP, en el que realizamos un cliente Ajax que envía un serie de valores por POST a un programa PHP, el cual valida el contenido de estos datos y devuelve un mensaje como respuesta.

Dentro del código incluiremos una sentencia que reverenciará al archivo que contiene la biblioteca Prototype:
<script type="text/javascript" src="prototype-1.6.0.3.js"></script>


El código refactorizado para la petición Ajax consistirá en una única función llamada peticionAjax, dentro de la que se realizará una instancia de Ajax.Request:

function peticionAjax() {
    var url = "http://localhost/validaDatos.php";
    var objParametros = creaObjParametros();
    var ajaxObjhttp = new Ajax.Request(url,
        method: 'POST',
        parameters: objParametros,
        onCreate: function(transport) {
                document.getElementById("divRespuesta").innerHTML = "<p>Iniciando nueva petición Ajax...</p>";
                },
        onSuccess: function(transport) {
                document.getElementById("divRespuesta").innerHTML += transport.responseText;
                },
        onFailure : function(response) {
                alert("Ocurrió un error al acceder al servidor Web");
                }
    });

function creaObjParametros() {
    var obj = new Object();
    obj.txtAutor = document.getElementById("txtAutor").value;
    obj.txtAnio = document.getElementById("txtAnio").value;
    obj.txtTitulo = document.getElementById("txtTitulo").value;
    return obj;
}


El la variable url y el objeto objParametros representan los parámetros url y parameters que definimos previamente. Podemos ver que se realiza una llamada a la función creaObjParametros, con la cual se obtiene un objeto que dentro de sus propiedades contiene cada uno de estos parámetros que serán enviados junto con la petición HTTP. Posteriormente encontramos la instancia al método Ajax.Request en la que se incluye la variable url como primer parámetro, después aparece la declaración de un objeto -indicado con la apertura y cierre de las llaves { }- dentro del cual se establecen de una serie de propiedades y métodos:

var ajaxObjhttp = new Ajax.Request(url,
    method: 'POST',
    parameters: objParametros,
    onCreate: function(transport) {
        document.getElementById("divRespuesta").innerHTML = "<p>Iniciando nueva petición Ajax...</p>";
    },
    onSuccess: function(transport) {
        document.getElementById("divRespuesta").innerHTML += transport.responseText;
    },
    onFailure: function(response) {
        alert("Ocurrió un error al acceder al servidor Web");
    }
});


Las propiedades representan el resto de los parámetros necesarios para la petición como son method (tipo de método de envío) y parameters (parámetros enviados junto con la petición), mientras que los métodos declarados representan las funciones que serán ejecutados en cada uno de los eventos de la petición, identificando estos métodos con el nombre del evento que representa, ya sea onCreate, onSuccess y onFaliure.

El código completo de la implementación dentro de la página HTML es el siguiente:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Petición Ajax con Prototype</title>
<script type="text/javascript" src="prototype-1.6.0.3.js"><!-- Referencia a Prototype--></script>
<script type="text/javascript">
function peticionAjax() {
    var url = "http://localhost/validaDatos.php"; //URL Recurso Web accedido
    var objParametros = creaObjParametros(); //Llamada a la función que devuelve los parámetros dentro de un objeto
    var ajaxObjhttp = new Ajax.Request(url, {
        method: 'POST',
        parameters: objParametros,
        onCreate: function(transport) { //Método ejecutado cuando se inicie la petición
                document.getElementById("divRespuesta").innerHTML = "<p>Iniciando nueva petición Ajax...</p>";
                },
        onSuccess: function(transport) { //Método ejecutado cuando la petición es completada sin errores
                document.getElementById("divRespuesta").innerHTML += transport.responseText;
                },
        onFailure : function(response) { //Método ejecutado cuando la petición es completada con algún error
                alert("Ocurrió un error al acceder al servidor Web");
                }
    });
}

function creaObjParametros() {//Función que almacena en un objeto los valores capturados en el formulario que serán enviados por POST a la página HTML
    var obj = new Object();
    obj.txtAutor = document.getElementById("txtAutor").value;
    obj.txtAnio = document.getElementById("txtAnio").value;
    obj.txtTitulo = document.getElementById("txtTitulo").value;
    return obj;
}
</script>
</head>
<body>
<form style="background-color:#EFEDF1">
    <label for="txtAutor">Autor:</label>
    <input type="text" id="txtAutor" name="txtAutor" size="40" /><br/>
    <label for="txtAnio">Año:</label>
    <input type="text" id="txtAnio" name="txtAnio" size="10" /><br/>
    <label for="txtTitulo">Títiulo:</label>
    <input type="text" id="txtTitulo" name="txtTitulo" size="50" /><br/>
    <input type="button" value="Validar datos" onclick="peticionAjax()" />
    </form>
<div id="divRespuesta"></div>
</body>
</html>


Para probarlo solamente deberemos comprobar la ubicación del archivo de la biblioteca Prototype, representada en el código con el nombre prototype-1.6.0.3.js, dado que en ejemplo se espera que este archivo se encuentre en el mismo directorio de la página HTML. De la misma forma debemos asegurarnos de que la ruta del archivo PHP validaDatos.php dentro del servidor Web es correcta. Por si desean tener otra vez a la mano este archivo PHP aquí les dejo su código:

validaDatos.php

<?
//Expresiones regulares para la validación de los datos
$er_titulo = "/^([a-z]|[A-Z]|[ÁÉÍÓÚáéíóúÑñÜü]|[0-9]|[\s\.\-,:'\"])+$/";
$er_autor = "/^([a-z]|[A-Z]|[ÁÉÍÓÚáéíóúÑñÜü]|[\s,\.'\-])+$/";
$er_anio = "/^([1-2][0-9][0-9][0-9])$/";
//Validamos que se hayan enviado todos los datos por post
if(isset($_POST['txtAutor']) && isset($_POST['txtAnio']) && isset($_POST['txtTitulo'])){
  //Validamos el contenido de los campos
  $autor = stripslashes(trim($_POST['txtAutor']));
  $anio = trim($_POST['txtAnio']);
  $titulo = stripslashes(trim($_POST['txtTitulo']));
  if(!preg_match($er_autor,$autor)){
    $campo = "autor";
  }
  else if(!preg_match($er_anio,$anio)){
    $campo = "año";
  }
  else if(!preg_match($er_titulo,$titulo)){
    $campo = "título";
  }
  else{
    $mensaje = "Todos los campos capturados son correctos :)";
  }
  if(isset($campo))
    $mensaje = "Error: El texto capturado en el campo $campo es incorrecto";
}
else{
  $mensaje = "No se recibieron los valores";
}
?>
<p><?=$mensaje?></p>
<p><u>Valores recibidos</u>:<br />
Autor: <?=$_POST['txtAutor']?><br />
Año: <?=$_POST['txtAnio']?><br />
Título: <?=$_POST['txtTitulo']?><br />
</p>


Una vez almacenados estos archivos podremos probarlos y comprobar que el cliente realiza la misma tarea que el original,



con la diferencia de que gracias a los eventos de Prototype se despliega un mensaje al iniciar la petición, y en caso de que ocurriera un error podríamos darnos por enterados gracias al mensaje de alerta que incluimos. Es cierto que toda esta funcionalidad la podríamos haber realizado nosotros mismos utilizando la clase XMLHttpRequest, pero habríamos tenido que invertir una cantidad de tiempo importante, por lo que en este momento es cuando podemos apreciar la gran ayuda que nos brinda Prototype, y no solo al ahorrarnos tiempo de trabajo sino que además nos permite organizar mejor nuestra programación con Ajax, al brindarnos una estructura prediseñada con la cual especificamos la configuración de la petición y la funcionalidad completa del proceso, con todo lo cual se vuelve realmente más fácil nuestra labor como programadores de clientes Ajax.

Como conclusión final puedo decir que vale la pena invertir tiempo en dominar nuevas herramientas para facilitar nuestro trabajo de programación, ya que esto nos ayuda a que podamos construir mejores proyectos de software en el futuro.