Invocando servicios en la web con WinJS.xhr

  • Por
En este artículo aprenderemos cómo realizarlo de forma sencilla y aprovecharemos la oportunidad para empezar a modificar el proyecto creado por la plantilla Grid de Visual Studio 2012.
Uno de los escenarios más habituales de cualquier aplicación, sea de escritorio o de web, es la invocación de servicios en Internet para recuperar información.

La función: WinJS.xhr

Muchas aplicaciones necesitan conectividad con Internet para realizar llamadas a servicios con objeto de recuperar información, o bien de actualizarla. En sitios y aplicaciones web cada día se utilizan con más frecuencia peticiones XmlHttpRequest. En las Aplicaciones Metro vamos a poder utilizar este método directamente, o bien elegir un enfoque más amigable: utilizar la función WinJS.xhr, que se encarga de ocultar toda la "fontanería" para ofrecernos simplemente el resultado en forma de objeto promise.

Este método recibe un objeto con distintas propiedades que representan sus opciones. Estas propiedades son:

  1. url (requerida): URL absoluta o relativa a la que se hace la llamada.
  2. type (opcional): especifica el verbo HTTP a usar, como GET, POST, HEAD, etc. Por defecto se usará GET.
  3. user (opcional): usuario en caso de ser necesario un proceso de autenticación. Si no se envía y la URL lo requiere, se mostrará una ventana para introducir credenciales.
  4. password (opcional): contraseña para el proceso de autenticación. Se ignorará si no se asigna un usuario en la propiedad anterior.
  5. headers (opcional): objeto cuyos nombres de propiedades se usan como nombres de cabeceras, y cuyos valores para dichas propiedades se usarán como valores de esas cabeceras.
  6. data (opcional): datos que se pasan internamente al método XMLHttpRequest.send().
El resultado de este método será un objeto promise al que, como de costumbre, podremos suscribir funciones para gestionar un resultado satisfactorio, erróneo, o bien el progreso en el proceso de invocación. En el siguiente fragmento de código podemos ver un ejemplo de uso de este método:

WinJS.xhr({ url: "http://www.desarrolloweb.com/rss/general_rss.php" })
.done(processPosts, processError, showProgress);

En el método processPost recibiríamos el resultado una vez la función se hubiera completado con éxito. Este resultado contendrá una propiedad responseXML de tipo DomParser del que podríamos extraer los resultados de la llamada en formato XML.

En cualquier caso, la mejor forma de verlo es ponerlo en práctica. Vamos a modificar el proyecto creado con la plantilla de Grid que hemos estado utilizando en los anteriores artículos de este manual.

Modificando Data.js

Lo primero que tenemos que hacer es abrir el fichero Data.js. En el anterior artículo de la serie hablamos en profundidad de él y de las partes de que se compone, por lo que se recomienda leerlo antes si no se ha hecho todavía.

En este fichero lo primero que vamos a borrar es todo el código que viene por defecto y que no vamos a utilizar. Los elementos que tenemos que eliminar son:

  1. groupDescription, itemDescription y itemContent.
  2. lightGray, mediumGray y darkGray.
  3. sampleGrops y sampleItems.
Con esto habremos eliminado toda la información que carga por defecto la plantilla de aplicación para que podamos ver algo en la pantalla sin tener que hacer nada. Es una especie de "Lorem ipsum" que no vamos a necesitar.

Eliminados estos datos, lo siguiente es definir las variables que vamos a utilizar para recuperar la información. En el siguiente fragmento podemos verlas:

// Blog que vamos a consultar
var blog = {
key: "DesarrolloWeb.com - General",
url: 'http://www.desarrolloweb.com/rss/general_rss.php',
title: 'tbd',
updated: 'tbd'
};

El objeto blog contendrá todos los datos que necesitamos para hacer la operación, incluida una referencia a una función acquirerSyndication que definiremos a continuación. En cuanto a las funciones que vamos a necesitar, la primera en definir será acquirerSyndication.

function acquireSyndication(url) {
// Invoca una URL y devuelve el promise
return WinJS.xhr({ url: url });
}

Esta función simplemente va a recibir una URL y va a utilizar WinJS.xhr para realizar una llamada a la misma, devolviendo el promise resultante.

Esta función tendremos que llamarla desde alguna otra. En este caso dicha función será getBlogPosts y se encargará de obtener los posts de un blog recibido como parámetro.

function getBlogPosts(blog) {
// Obtiene los posts de un blog pasado como parametro
var blogPosts = new WinJS.Binding.List();
acquireSyndication(blog.url).then(function (articlesResponse) {
var articleSyndication = articlesResponse.responseXML;

// Calcular titulo y la fecha de ultima publicacion
blog.title = articleSyndication.querySelector("rss > channel > title").textContent;
var published = articleSyndication.querySelector("rss > channel > item > pubDate").textContent;
blog.updated = formatLastModifiedDate(published);

// Procesar los items del canal RSS
getItemsFromXml(articleSyndication, blogPosts, blog);
});

return blogPosts;
}

La función realiza tres pasos sencillos:

  1. Obtener el contenido de sindicación del blog a partir de su URL mediante el método acquirerSyndication.
  2. Recuperar título y fecha de última modificación del blog. En este caso se están utilizando selectores basados en la estructura RSS que tiene el feed de la URL de DesarrolloWeb.com que estamos utilizando. Hay que tener en cuenta que si, por ejemplo, el canal fuera ATOM, estos selectores cambiarían.
  3. Procesar todos los ítems del blog con el método getItemsFromXml, que definiremos a continuación.
Una posible implementación de formatLastModifiedDate, la podemos ver en el siguiente fragmento.

function formatLastModifiedDate(lastUpdate) {
// Convierte la fecha al formato en que queremos mostrarla
var date = new Date(lastUpdate);
var dateFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter(
"month.abbreviated day year.full");
var blogDate = dateFmt.format(date);
return "Last updated " + blogDate;
}

Como vemos, WinRT nos va a ofrecer clases y métodos para poder realizar estas transformaciones de forma sencilla, simplemente pasándoles las máscaras deseadas. La última función que necesitamos definir es getItemsFromXml, que nos permitirá obtener la información concreta de los posts.

function getItemsFromXml(articleSyndication, blogPosts, feed) {
// Obtener todos los posts del blog
var posts = articleSyndication.querySelectorAll("item");
for (var postIndex = 0; postIndex < posts.length; postIndex++) {
var post = posts[postIndex];

// Obtener datos de cada post: titulo, fecha, descripcion y link
var postTitle = post.querySelector("title").textContent;
var postPublished = post.querySelector("pubDate").textContent;
var description = post.querySelector("description").textContent;
var link = post.querySelector("link").textContent;

// Guardar en la lista enlazada de posts
blogPosts.push({
group: feed,
key: feed.title,
title: postTitle,
published: postPublished,
link: link,
description: description
});
}
}

Como en el método anterior, tenemos tres pasos fundamentalmente:

  1. Obtener todos los posts para procesarlos uno a uno, mediante el selector correspondiente. Aquí, como en el caso que comentábamos anteriormente, dependerá del tipo de canal RSS.
  2. Para cada post, extraer la información concreta de título, fecha de publicación, descripción y enlace.
  3. Finalmente, guardar estos posts en la lista correspondiente para poder utilizarla posteriormente en el binding de datos.
En este punto ya tendríamos toda la información recuperada, procesada y almacenada en la lista que devuelve el método getBlogPosts. Ahora sólo nos queda un último paso: invocarlo. Para ello deberemos, en primer lugar, borrar este fragmento de código que está justo antes de la definición de los elementos del espacio de nombres Data.

var list = new WinJS.Binding.List();
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);

// TODO: Replace the data with your real data.
// You can add data from asynchronous sources whenever it becomes available.
sampleItems.forEach(function (item) {
list.push(item);
});

Ese código será remplazado por este otro:

var list = getBlogPosts(blog);
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);

En él simplemente estamos llamando al método getBlogPosts, que ya nos devuelve la lista preparada para utilizarla. La llamada al método la hacemos a partir de la información que definimos al principio del artículo en la variable blog.

Sólo nos queda ejecutar la aplicación y ver los resultados. Deberían ser parecidos a lo que podemos ver en la siguiente imagen:

La buena noticia es que estamos recuperando la información, puesto que el grid mostrará la lista de elementos que realmente define el canal RSS. La mala es que el resultado, desde un punto de vista estético, es francamente poco agradable. Pero esto tiene fácil solución, y pasa por Blend for Visual Studio.

Conclusiones

Hemos aprendido cómo se realizan llamadas a servicios en Internet con Aplicaciones Metro mediante WinJS.xhr. Además hemos puesto en práctica este conocimiento en un escenario básico en el que hemos empezado a modificar las plantillas que vienen por defecto con Visual Studio 2012 para adaptar los resultados a nuestras necesidades.

En próximos artículos veremos cómo podemos mejorar el aspecto de la aplicación de forma sencilla utilizando Blend para realizar su diseño gráfico.

Bibliografía

Centro de Desarrollo de Windows (español)