En una anterior entrada comenzamos a crear nuestro Servicio Web con Xojo. De hecho la primera de las entregas estuvo dedicada a la parte del backend (del servidor), creando la aplicación Xojo que actuaría como capa intermedia entre los clientes y la base de datos que, finalmente, contiene la información con las que se trabaja. Y si bien en este ejemplo se utiliza SQLite como motor de base de datos, no te supondrá mayor problema modificarlo para que funcione correctamente con cualquiera de los soportados: PostgreSQL, MySQL (MariaDB), Oracle, SQL Server o bien cualquier otra fuente de datos accesible mediante ODBC (caso de Access).
En esta seguna parte de la entrega nos centraremos en la creación de cliente de escritorio, encargado de realizar peticiones (consultas) utilizando la API definida por nuestro Servicio Web, así como enviar datos hacia nuestra aplicación de servidor Xojo para que añada nueva información a la base de datos.
Los aspectos fundamentales en este caso son:
- La creación de una subclase a partir de Xojo.net.HTTPSocket
- Tratamiento de los datos recibidos como respuesta de la petición realizada
- Codificación de JSONItem a MemoryBlock para su envío
Ahora bien, tal y como ya se indicó en la anterior entrega, has de tener en cuenta que he dejado fuera del código todo tipo de controles de errores, verificaciones y demás cuestiones que normalmente querrás incluir en tus proyectos, ya sea para sanear la entrada realizada por el usuario, verificar que las estructuras de datos son las esperadas, etc.
Crear subclase de HTTPSocket
Xojo nos proporciona dos clases para realizar peticiones utilizando el protocolo de comunicaciones HTTP: HTTPSocket en el Framework clásico y Xojo.Net.HTTPSocket disponible en el nuevo framework en el que se refuerza el uso de espacios de nombres (tal y como ocurre por ejemplo en Java y otros lenguajes). Ambas son, a su vez, clases derivadas de la clase TCPSocket, si bien Xojo.Net.HTTPSocket soporta HTTP 1.1 mientras que HTTPSocket soporta la versión HTTP 1.0. En este tutorial utilizaremos por tanto Xojo.Net.HTTPSocket, no sólo por el soporte de la versión más reciente del protocolo, sino también por otras ventajas.
¡Empezamos! Y lo haremos creando un nuevo proyecto de Escritorio. En el IDE añade una nueva clase (Insert > Class) y utiliza el panel Inspector para asignar su tipo de dato como Xojo.Net.HTTPSocket, tal y como se muestra en la siguiente imagen:
A continuación, y con la recién creada clase aun seleccionada en el Navegador de Proyecto (la columna situada más a la izquierda en el IDE), añadiremos una propiedad (Insert > Property). Utilizaremos esta propiedad para contener una referencia al listado de nuestra interfaz de usuario que vayamos a actualizar con los datos recibidos desde nuestro Servicio Web. Por tanto, con la propiedad seleccionada, utiliza el panel Inspector para definir su tipo y nombre (la etiqueta de texto que usamos en el código para referirnos a dicha propiedad):
Fíjate en que hemos definido el ámbito de la propiedad como Private (Privado). Esto significa que sólo podrán acceder a dicha propiedad las instancias creadas a partir de la propia clase, y no las instancias de cualquier otro tipo de objeto o incluso de las clases creadas a partir de la clase que estamos definiendo; es decir, aquellas clases que heredan tanto las propiedades como métodos de su jerarquía de clases.
HTTPSocket: comunicaciones asíncronas
¿Por qué querríamos tener una referencia al listado (control de la UI) en nuestra subclase de HTTPSocket? Como en cualquier problema, siempre podemos tomar múltiples enfoques para solucionarlo. En este caso se trata de un modo muy conveniente de tener la referencia del control a actualizar con nuevos datos a mano, dado que utilizaremos nuestro Socket HTTP en modo asíncrono.
Esto es, una vez que lancemos la petición contra nuestro servicio web no bloquearemos la ejecución de la aplicación, permitiendo así que el usuario del cliente pueda continuar realizando otras operaciones. Será la propia clase, a través del evento PageReceived quien nos indique de la recepción de los datos enviados por parte del Servicio Web, de modo que podamos operar sobre ellos.
Precisamente esto será lo que hagamos a continuación. Con nuestra clase Xojo.Net.HTTPSocket aun seleccionada en el Navegador de Proyecto, añade un nuevo Evento (Insert > Event Handler…). En la ventana resultante, selecciona en el listado la entrada correspondiente a PageReceived y confirma la selección. Dicho evento se añadirá a la clase bajo la sección Event Handlers.
A continuación, y con el evento recién añadido seleccionado en el Navegador de Proyecto para acceder al Editor de Código asociado, introduce el siguiente código:
if httpStatus = 200 then if Content.Size <> 0 then listado.DeleteAllRows dim contenido as text = Xojo.Core.TextEncoding.UTF8.ConvertDataToText(content) //definimos la codificación para los datos recibidos dim items as xojo.core.dictionary = xojo.data.ParseJSON(contenido) //creamos un diccionario a partir del contenido en formato JSON items = items.Value("AllAlbums") //obtenemos los elementos reales por la clave 'AllAlbums' for each item as xojo.Core.DictionaryEntry in items //iteramos dim tempDict as xojo.core.Dictionary = item.Value //recuperamos el diccionario (elemento JSON) del item dim titulo as text = tempDict.Value("title") //accedemos al valor deseado listado.AddRow titulo next end if end if
Como ves, el código está comentado para indicar la función de cada una de las principales líneas. Conviene destacar, sin embargo, que el evento PageReceived nos proporciona una serie de parámetros de partida a través de los cuales podemos acceder a toda la información que precisamos para trabajar con los datos enviados desde nuestro Servicio Web:
- URL: El URL que nos ha devuelto la respuesta a nuestra petición.
- HTTPStatus: El código de estado HTTP, y que conviene consultar para verificar el resultado de nuestra petición.
Content: Se trata de un MemoryBlock que puede contener datos asociados a la respuesta, ya sean binarios o en formato de texto
Otro detalle interesante es el uso de Xojo.Core.Dictionary en vez del tipo de dato Dictionary disponible en el framework clásico de Xojo. La ventaja de utilizar el primero, entre otras, no es otra sino la posibilidad de iterar con una mayor facilidad a través de sus contenidos utilizando el iterador (Xojo.Core.DictionaryEntry) obtenido a partir del mismo.
Si lo deseas también puedes añadir un evento adicional a la subclase HTTPSocket que estamos definiendo: Error. Este será el invocado en el caso de que haya ocurrido algún tipo de error en las peticiones realizadas a través de la instancia del socket.
Una vez definida la propiedad y los eventos, es el momento de añadir un par de métodos y que serán los encargados de actuar en nuestro HTTPSocket para realizar las peticiones propiamente dichas, guardando en este ejemplo una relación directa con las operaciones que queremos realizar sobre la API del Servicio Web creado en la primera entrega de este tutorial.
Con la clase aun seleccionada en el Navegador de Proyecto, añade un nuevo método (Insert > Method), introduciendo en el Panel Inspector los datos mostrados en la siguiente imagen. Este será el método encargado de solicitar al Servicio Web que nos devuelva el nombre de todos los álbumes disponibles (por tanto, esperamos obtener la respuesta a través del evento PageReceived):
Como puedes ver, este método espera recibir un control de tipo ListBox —al que accederemos mediante la variable local ‘l’—, así como el texto correspondiente al URL sobre el quer deseamos realizar la petición —al que accedemos mediante la variable local ‘elurl’— Una vez definida su signatura, introduce el siguiente código para el método recién creado:
listado = l self.send("GET",elurl)
Sencillo, ¿verdad? Como puedes ver, lo único que hacemos es guardar en la propiedad de la instancia la referencia al control ListBox que utilizaremos posteriormente cuando se ejecute el evento PageReceived para actualizar sus contenidos (consulta el código correspondiente a dicho evento). Posteriormente, utilizamos el método Send de la clase HTTPSocket pasando el verbo “GET” y el URL sobre el que deseamos realizar la petición propiamente dicha. Al tratarse de una operación asíncrona, dejaremos a que el código del objeto reaccione cuando llegue el momento mediante los eventos PageReceived o Error, según sea el caso (consulta el resto de eventos disponibles en la documentación de Xojo).
HTTPSocket: Enviar datos junto con la petición
Añade un nuevo método utilizando en el Panel Inspector asociado los valores mostrados en la siguiente imagen. Este es el método que nos permitirá enviar datos hacia el Servicio Web para que sean incorporados a la base de datos:
En esta ocasión, el método espera recibir un objeto de tipo JSONItem, al que podremos acceder mediante la variable local ‘item’; así como una instancia de tipo Text y que se corresponderá con el URL sobre el que deseamos realizar la petición, al que podremos acceder mediante la variable local ‘elURL’. Escribe el siguiente código en el Editor de Código asociado al método:
dim d as xojo.Core.MemoryBlock = xojo.core.TextEncoding.utf8.convertTextToData(item.ToString.ToText) self.SetRequestContent(d,"application/json") self.Send("POST",elURL)
Los HTTPSocket nos permiten asociar información adicional (datos) en el envío de una petición, más allá de la indicada en las cabeceras HTTP (Head) mediante el método SetRequestContent. Dicho método requiere el paso de dos parámetros: un bloque de memoria con los datos a adjuntar, y la definición del tipo de dato enviado (tipo MIME) como un objeto Text.
En el código, nos encargamos de generar el bloque de memoria a partir del JSONItem recibido como parámetro de entrada, y que es la estructura de datos correspondiente al registro que deseamos añadir a la base de datos. La segunda línea se encarga de asociar los datos a la petición; y en la última línea de código realizamos la petición sobre el URL indicado por la variable local ‘elURL’ y utilizando en este caso el verbo “POST”.
Diseñar la interfaz de usuario
Con lo anterior hemos terminado con la definición de nuestra subclase basada en HTTPSocket. Ahora es el momento de definir una interfaz de usuario mínima, simplemente para comprobar la funcionalidad de nuestro cliente contra el Servicio Web.
Selecciona la ventana Window1 y realiza un diseño como el mostrado en la siguiente imagen, utilizando para ello un ListBox, dos objetos TextField y dos PushButton:
A continuación, añadiremos una instancia (objeto) basado en la clase basada en HTTPSocket. Para ello sólo tendremos que arrastrarla desde el Navegador de Proyecto y soltarla en el Editor de Ventana. Verás que quedará añadida en el Tray o barra inferior del editor, tal y como muestra la siguiente imagen:
En cuanto a la interfaz de usuario, los únicos controles que tendrán código asociado serán sendos PushButton. Selecciona el primero de ellos (etiquetado “Consulta”), añade el evento Action, e introduce el siguiente código en el Editor de Código asociado:
MySocket1.getRequest(Listbox1, "http://127.0.0.1:8080/Api/GetAll")
Como puedes ver, se encarga de invocar el método ‘getRequest’ sobre la instancia basada en nuestra clase, pasando como parámetros el control “ListBox1” y el texto correspondiente al URL sobre el que deseamos realizar la petición.
Observa que en este caso se trata de una dirección IP privada, dado que tanto el Servicio Web como el cliente estarán ejecutándose sobre el mismo equipo (modifica este dato para adecuarlo a tus necesidades). Observa también que se utiliza como parte de la Ruta en el URL el texto “/Api” y que es requerido para que el Servicio Web basado en Xojo pueda atrapar la petición mediante el evento HandleSpecialURL (consulta la primera entrega del tutorial). De igual modo, la ruta incluye el método “GetAll” definido por la API del Servicio Web que hemos creado en Xojo.
Añade a continuación el evento Action en el PushButton2 (etiquetado “Envío), introduciendo el siguiente código en el Editor de Código asociado:
dim d as new Dictionary d.Value("Title") = TextField1.Text d.value("ArtistId") = TextField2.Text dim item as new JSONItem item.Value("newAlbum") = d MySocket1.sendItem(item,"http://127.0.0.1:8080/Api/AddAlbum")
Como ves, no es nada especial. Simplemente creamos un diccionario utilizando las claves que espera encontrar posteriormente el Servicio Web a la hora de acceder a los nodos que contienen los diferentes datos del registro. Por otra parte, puedes ver con qué facilidad se puede crear un JSONItem a partir de un Dictionary.
Por último, el código utilizará el método “sendItem” sobre la instancia creada a partir de la clase que hemos definido, pasando sobre dicho método los parámetros esperados: una instancia JSONItem y el texto correspondiente al URL sobre el que deseamos realizar la petición. Como en el anterior caso, observa que estamos invocando el método “AddAlbum” definido por la API del Servicio Web que hemos creado con Xojo.
¡Listo para atender tus peticiones!
Ya hemos finalizado con la creación del cliente. Ejecuta el Servicio Web, ejecuta a continuación el Cliente y pulsa el botón de envío y/o recepción. Verás que con el de consulta recibirás todas las entradas de álbumes disponibles en la base de datos de ejemplo, mientras que con el de Envío podrás insertar en la base de datos los valores introducidos en sendos campos de texto.
En definitiva, Xojo nos permite crear en tiempo récord y de forma realmente sencilla tanto el componente servidor del backend como clientes multiplataforma que puedan utilizarlo para adecuarlo a nuestras necesidades.
Las aplicaciones WebService y Cliente que hemos visto en estas dos entregas no suponen, ni mucho menos, aplicaciones completas; sin embargo te servirán como puntos de partida para utilizarlas en tus propios proyectos.
[…] Puedes leer la segunda parte de este tutorial en esta entrada del blog. […]
Gracias Javier, por tu excelente trabajo y tu compromiso con la Comunidad XOJO en Español, la verdad que tus aportes son muy importantes. Un gran abrazo desde Argentina.
Muchas gracias Ruben! Aprovecho para recomendaros que os paséis por los Hangout de Xojo en Español 😉 Por ejemplo, mañana 12/5/22 Ricardo nos contará como utilizar WebSDK para ampliar la funcionalidad de proyectos Xojo Web… y en el siguiente veremos cómo utilizar Xojo para utilizar la API de WooCommerce!