Crear Servicio Web con Xojo, Parte I

Web ServiceCon Xojo Web, así como con las licencias Xojo Pro y Xojo Enterprise encontramos el modo más rápido y eficiente de crear cualquier servicio web que necesitemos para clientes multiplataforma.

Cuando adquirimos nuestra licencia de Xojo Web muy probablemente la utilicemos para crear completas aplicaciones web sin la necesidad de aprender todo un arsenal de lenguajes interpretados y los frameworks que, de otro modo, habríamos de manejar para llevarlas a cabo; ya sabes, los consabidos HTML, CSS, JavaScript, PHP, etc. Sin embargo, también es la edición que nos procura las herramientas para crear servicios web que actúen como middleware entre los clientes (consumidores de la API) y la base de datos, por ejemplo.

Es decir, se trata de crear un servidor que reciba peticiones desde nuestros clientes multiplataforma (que también podemos crear en Xojo, claro), capaz de procesar la entrada y realizar la acción asociada; desde la actualización de la información recibida en una base de datos, la devolución de los datos hacia el cliente en base a la petición, etc. En definitva, crear una API de forma sencilla y ¡en tiempo récord!

En este turorial (dividido en dos partes, por su longitud) veremos precisamente esto, creando un servicio Web sencillo que nos mostrará los aspectos fundamentales a tener en cuenta, así como un cliente Desktop (podría ser iOS sin mayor problema), con el cual realizaremos la conexión con nuestro servicio web y también utilizarmos para realizar las peticiones sobre el mismo.

Antes de empezar, aclarar que este tutorial no contempla todas las consideraciones relacionadas con la comprobación de errores, así como la sanitización de datos en las entradas y salidas, con el objeto de minimizar el máximo posible el código mostrado para que los conceptos que interesan queden, así, lo más claro posibles. No obstante, puedes consultar otras entradas sobre estos aspectos, como por ejemplo proteger nuestras aplicaciones frente a ataques por Inyección SQLite o la gestión de Excepciones, entre otros.

Además, para seguir los pasos de este tutorial necesitarás la base de datos de ejemplo por excelencia: Chinook Database (renombrada en este ejemplo a “test.sqlite”)

Servicio Web en la práctica

Comenzaremos creando el servicio web. Para ello, abre el IDE de Xojo y crea un nuevo proyecto con el nombre que desees (por ejemplo “WebService”, bastante apropiado). Verás que si bien Xojo añade por omisión una página al proyecto en realidad nuestro servicio web no precisa de interfaz gráfica. Aun así, debes dejarla dado que se requiere por el proyecto de Xojo tipo Web.

Todo lo que necesitamos para llevar a cabo este tutorial colgará del objeto App (¡un Singleton en toda regla!) De hecho comenzaremos por añadir una propiedad que será la encargada de contener la referencia a la instancia de nuestra base de datos SQLite. Selecciona el objeto App en el Navegador de Proyecto (la columna situada más a la izquierda en el IDE de Xojo) y utiliza a continuación Insert > Property. Con la propiedad recién añadida seleccionada, accede al panel Inspector para definir su nombre, tipo y ámbito tal y como puedes ver en la siguiente imagen:

Propiedad apuntando a instancia SQLIteUna vez que hemos añadido la propiedad, nos encargaremos de crear y asignar a la misma la instancia de una SQLiteDatabase que apunte al archivo de base de datos SQLite utilizado en el ejemplo. Para ello, y con el objeto App seleccionado, añade el evento Open con Insert > Event. En el Editor de Código resultante incluiremos lo siguiente:

dim f as FolderItem = SpecialFolder.Desktop.Child("test.sqlite")

if f.Exists then

database = new SQLiteDatabase

database.DatabaseFile = f

call database.Connect

end

if database.Error then MsgBox "Error conectando a la base de datos"

Como puedes ver, se trata de un fragmento de código similar al utilizado en otras ocasiones para crear una instancia SQLiteDatabase, asociar el archivo de bases de datos SQLite propiamente dicho y establecer la conexión para poder operar sobre él desde nuestra app.

La magia de HandleSpecialURL

Los proyectos Web creados con Xojo nos proporcionan dos vías de gestionar las peticiones recibidas mediante eventos, la primera de ellas es con HandleURL; es decir, el que se dispara cada vez que un cliente (navegador web o aplicación cliente) escribe el URL o la dirección IP en la que está escuchando nuestra aplicación Web. Si estuviésemos creando un App Web, este es el evento que querríamos utilizar para atender las peticiones de páginas.

Sin embargo, para los servicios web encontraremos más apropiado añadir al proyecto el evento HandleSpecialURL. La principal particularidad de este evento, a diferencia del anterior, es que podremos emplear formatos de URL típicos de las API web, donde los métodos (es decir la acción solicitada por los clientes) forma parte del propio URL. Como única precaución a tener en cuenta es que los URL deben de incorporar como parte de la ruta los componentes “Special” o “API”. Además, cuando creamos apps o servicios web, este evento también nos permitirá atender los callback de otros servicios web, como pueda ser por ejemplo PayPal.

Por ejemplo, un URL válido para que pueda ser capturado y procesado por HandleSpecialURL sería:

http://www.midominioweb.com/api/getCustomers

Donde “getCustomers” se correspondería en este caso con uno de los métodos de nuestra API.

Por tanto, con el objeto App seleccionado, utilizaremos Insert > Event para añadir el evento HandleSpecialURL, tal y como se muestra en la siguiente imagen:

Añadir eventos HandleSpecialEvent a app Web, ideal para crear un Servicio WebComo podrás comprobar una vez añadido el evento, cada vez que se dispare dicho evento recibiremos en la variable “Request” un parámetro de tipo WebRequest, además de que esperará que devolvamos un Booleano como respuesta con True para que se procese nuestra respuesta, o en caso contrario (False, por omisión) para que se ignore la respuesta.

Request, toda la información de la petición recibida

Precisamente, encontraremos en el objeto Request todo lo que necesitamos saber para atender, procesar y (de ser necesario) responder a la petición recibida por nuestro servicio web. Por ejemplo, mediante la propiedad “Path” obtendremos la parte de la ruta en el URL, excluyedno “/special/” o “/api/”, así como cualquier consulta formulada directamente como parte del URL mediante el signo “?”. Por ejemplo en el URL:

http://www.midominioweb.com/api/getCustomers?spain

La propiedad Request.Path nos devolverá el String “getCustomers”; de modo que, a partir de aquí, nuestro servicio web podrá ejecutar un código u otro en función del método API recibido.

Enviar y Recibir datos JSON

Para no extender mucho este artículo, la API de nuestro servicio web consistirá únicamente en dos métodos: “GetAll” y “AddAlbum”. Con el primero, el cliente (consumidor) del servicio web obtendrá en formato JSON el nombre correspondiente a cada uno de los álbumes almacenados en la base de datos, mientras que con el segundo método el consumidor de nuestro servicio web nos pedirá que añadamos un nuevo registro (nuevo álbum) precisamente a la tabla Albums en la base de datos de ejemplo.

¿Cómo recibimos y podemos procesar los datos asociados a una petición en el evento? Pues bien, aquí es donde nos resultará muy útil otra de las propiedades del objeto Request. La propiedad Entity contiene los datos enviados como parte de la petición y que no estén incorporados en las cabeceras. Por lo general se incluyen datos adicionales a las peticiones web empleando los verbos “PUT” y “POST”.

Teniendo todo lo anterior en cuenta, y con vistas a nuestro servicio de ejemplo, el código que deberemos de añadir al evento HandleSpecialURL sería el siguiente:

dim data as string = Request.Entity.DefineEncoding(encodings.UTF8)
dim entrada as JSONItem = new JSONItem(data)
dim salida as JSONItem
select case Request.Path

case "GetAll"

salida = GetAllAlbums

Request.Print( salida.ToString )

case "AddAlbum"

addNewAlbum(entrada.Value("newAlbum"))

end select

Return true

Lo que conviene significar es que asignaremos a la variable “data” los datos recibidos como parte de la petición (si hubiese), atendiendo en todo caso a contar con una codificación conocida para no tener problemas (UTF8). Luego, como licencia de nuestro ejemplo, presupondremos que siempre recibiremos datos adicionales en formato JSON, de modo que crearemos una nueva instancia JSONItem a partir de dichos datos.

Observarás que utilizamos una estructura Select Case para decidir qué método del servicio web en función de cuál sea el método de la API recibido como parte de la ruta y que, como hemos visto anteriormente, está almacenada en la propiedad Path. Así, si en la petición a nuestro Servicio Web es el método “GetAll”, llamaremos a nuestro método GetAllAlbums encargado de procesarlo y que devuelve un JSONItem con la respuesta (recuerda, el título de cada álbum en la base de datos de ejemplo).

¿Como enviamos la respuesta de una petición? Realmente simple: invocando el método Print sobre la petición recibida (recuerda, el parámetro “Request”) y pasando como argumento el texto que deseemos enviar; en nuestro ejemplo, se trata del JSONitem apuntado por la variable “Salida”.

Si recibimos una petición con el método “AddAlbum” de nuestra particular API, entonces simplemente nos encargaremos de llamar al método addNewAlbum de nuestra aplicación pasando como argumento el JSONItem correspondiente a los datos recibidos junto con la petición (recuerda, apuntado por la variable “entrada”). Para ello, nos encargamos de pasar el registro con la información que nos interesa y que cuelga del nodo raiz “newAlbum” en el JSONItem recibido.

Servicio Web: transacciones con la base de datos

Si en HandleSpecialURL es donde procesamos la petición recibida, reservamos en este caso a los métodos específicos de nuestra API la capacidad de actuar como nexo con la base de datos de ejemplo, ya sea para recuperar la información solicitada por la petición o bien para insertar (o modificar) los nuevos datos enviados por el cliente del servicio web.

Selecciona nuevamente el icono correspondiente al objeto App y, en este caso, utiliza Insert > Method para añadir un nuevo método, “getAllAlbums”, para el que utilizaremos la signatura indicada en la siguiente imagen:

Método de Servicio WebEl código encargado de generar el JSONitem resultante (y que “imprimiremos” como parte de la respuesta a la petición), con todos los nodos correspondientes a cada uno de los registros de la tabla Album de nuestra base de datos es el siguiente:

dim rc as RecordSet = database.SQLSelect("Select * from album order by title asc")

dim item as new JSONItem

if rc.RecordCount > 0 then

While not rc.EOF

dim d as new Dictionary

d.Value("artistid") = rc.Field("artistid").StringValue
d.Value("title") = rc.Field("title").StringValue

item.Value(rc.field("albumid").StringValue) = d

rc.MoveNext

Wend

rc.Close

end if

dim salida as new JSONItem

salida.Value("AllAlbums") = item

Return salida

A continuación crearemos el método  “addNewAlbum”, y que nos permite insertar un nuevo registro en la base de datos a partir de los datos recibidos como parte de la petición, utilizando para ello la signatura mostrada en la siguiente imagen:

Método para añadir nuevo registro en el Servicio WebEl código asociado a dicho método será el siguiente:

dim title as string = item.Value("Title")
dim artistid as string = item.Value("ArtistId")

database.SQLExecute("insert into album(title,artistid) values('"+title+"',"+artistid+")")

Como puedes observar el código es bastante sencillo; se limita a recuperar los valores asociados a cada una de las clave en el nodo JSONitem utilizándolos a continuación en la instrucción SQL encargada de añadir un nuevo registro a la base de datos.

Un Servicio Web… ¡Listo para Servir!

Como has visto, ¡el código necesario y estructura de nuestro servicio son realmente mínimos! Eso sí, teniendo en cuenta que hemos dejado fuera los elementos de comprobación y verificación habituales. Aún así, da una clara idea del tipo de capacidades, posibilidades y cuan sencillo podemos crear aplicaciones servidor con el lenguaje de programación y entorno de desarrollo Xojo.

En el vídeo adjunto podrás ver como la parte del cliente se comunica, envía y recibe datos utilizando este servicio web (así como otros detalles de despliegue del servicio web propiamente dicho). La creación del cliente será, precisamente, lo que veamos en la segunda parte que completa esta entrada sobre la creación de un servicio web con Xojo.

Puedes leer la segunda parte de este tutorial en esta entrada del blog.

6 comentarios en “Crear Servicio Web con Xojo, Parte I

  1. Juan

    Hola Javier,

    En primer lugar agradecerte el artículo, está muy claro y ya era hora de tener en nuestro idioma artículos de este tipo.

    Me ha servido muchísimo hacer el ejemplo que muestras.

    Me gustaría preguntarte una cuestión que me trae de cabeza unos días.

    Aprovechando el ejemplo he tratado de conectarme al servicio web desde una aplicación móvil a través de ajax y me lanza algunos errores.

    ¿Hay alguna forma de poder configurar las cabeceras del servidor que se activa con el servicio web?

    ¿Me podrías decir si has probado a conectarte con JavaScript al servicio web? y cómo podría hacerlo.

    Muchas gracias por todo.

    Saludos.

    1. Javier Rodriguez

      Estimado Juan,
      No creo que tenga mayor importancia el lenguaje utilizado para los clientes… dado que estos lanzarán las peticiones y esperarán los datos devueltos por el servidor (servicio web) también de forma independientemente al lenguaje utilizado en su programación. Lo que quiero decir con ello es que las comunicaciones HTTP funcionan en base a estándares y lo único que sirve de nexo es la parte que se expone… es decir, la API web.

      ¿Podría realizar un cliente JavaScript peticiones contra el servicio web? Seguramente, siempre y cuando los datos se devuelvan utilizando estándares. Es lo mismo que ocurre, por ejemplo, entre clientes JavaScript realizando peticiones contra servicios basados en Python…

  2. Eduardo Moreno

    Saludos:
    Gracias por explicar tan claro como desarrollar el objeto, pero dada mi novatez, debo preguntar:
    ¿La aplicación debe estar en un directorio llamado “api”?
    Esto debido a que haces referencia a http://www.midominioweb.com/api/getCustomers y que la propiedad Path elimina todo hasta api.

    1. Javier Rodriguez

      Hola Eduardo,

      No. Puedes elegir el directorio que desees… En este caso utilicé esa ruta para dejar más patente el objeto del ejemplo (una API). Eso sí, es conveniente la estructura de directorios para poder alojar varias apps desarrolladas con Xojo y que no colisionen entre sí. Esto es algo que también es habitual al usar otros frameworks / lenguajes de desarrollo web o bien “paquetes”.

      Javier

  3. mauricio tanco

    Como le doy acceso a terceros a traves de PHP por ejemplo a que puedan consumir mis servicios web?

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *