JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

JSONP (II): Soporte desde ASP.NET AJAX 4.0

En mi post de la semana pasada presenté el concepto de JSONP (o JSON with Pading) viendo lo útil que éste podía ser para acercarnos a la visión de servicios Web verdaderamente integrados entre dominios diferentes, y desde el lado de cliente.

En esta ocasión voy a comentar cómo sacarle partido desde nuestras aplicaciones creadas con ASP.NET AJAX y el soporte nativo que se ha incluido en la próxima versión 4.0 del framework.

El soporte de JSONP desde ASP.NET 4.0 y Visual Studio 2010 es transparente para el programador, ya que basta con incluir dentro de un ScriptManager una referencia a un servicio ubicado en otro dominio para que éste asuma automáticamente que debe hacer la llamda con JSONP. Por ejemplo:

<asp:ScriptManager ID="ScriptManager1" runat="server">
  <Services>
    <asp:ServiceReference Path="http://www.miotrodominio.com/Servicios/Inventario.svc" />
  </Services>
</asp:ScriptManager>

Es así de fácil. Simplemente no se quejará y asumirá que en el otro extremo existe un servicio al que se le puede pasar un parámetro "callback" para indicarle el nombre de la función de retorno para envolver la respuesta JSON.

Otro lugar en el que se ha incluido este soporte para JSONP es en la clase Sys.Net.WebServiceProxy de las bibliotecas JavaScript (de lado cliente, vamos) de ASP.NET AJAX. Esta clase es la que se utiliza por debajo para hacer llamadas a servicios Web, generalmente con XMLHttpRequest. En la nueva versión de ASP.NET esta clase posee dos nuevas propiedades creadas ex profeso para este propósito:

  • enableJsonp: indica al proxy si debe usar JSONP para realiar la llamada o no. Si es true se añadirá un parámetro de callback a la llamada y se realizará con una referencia a Script en lugar de usar el objeto especializado XmlHttpRequest.
  • jsonpCallbackParameter: establece o lee el nombre del parámetro de callback para la llamada JSONP. Es el nombre de nuestra función de JavaScript encargada de procesar la respuesta y que por lo tanto el servicio usará para envolver los datos JSON que nos envía.

Además, el método invoke de la clase añade dos parámetros opcionales al final con el mismo nombre que estas dos propiedades y su mismo nombre, para dar el soporte directamente en la llamada, sin necesidad de establecer las propiedades antes.

Por supuesto, podemos usar código propio que genere dinámicamente etiquetas de Script para realizar las llamadas JSONP, lo cual es muy sencillo y funciona en cualquier página HTML, sin necesidad de usar una biblioteca especial como ASP.NET AJAX, y en muchos casos será lo que hagamos. Es lo que veremos en un ejemplo algo más abajo.

Crear nuestros propios servicios JSONP

Existen multitud de servicios en Internet que ya utilizan JSONP como formato de retorno, de forma que se fomenta su uso desde cualquier página HTML. Luego veremos un ejemplo. Pero, ¿y si queremos crear nuestro propio servicio habilitado para JSONP?

En MSDN hay un ejemplo concreto de cómo hacerlo mediante la inclusión de codificador (MessageEncoder) y un comportamiento de operación (OperationBehavior) propios. Básicamente se trata de detectar la existencia del parámetro adicional en la petición y actuar en consecuencia para envolver en la llamada JavaScript los datos resultantes. No voy a entrar en detalles, pero si tienes interés puedes leer cómo hacerlo aquí.

Ejemplo práctico con Delicio.us

Delicio.us es el servicio de Social Bookmarking más popular del mundo. Gracias a él podemos descubrir interesantes enlaces que otra gente ha almacenado y  puesto a disposición de los demás. Podemos navegar y ver los enlaces más recientes, los más populares, los de un usuario concreto, o investigar enlaces buscando por "tags" o etiquetas que la gente usa para clasificarlos.

En la documentación de la API de Delicio.us se ofrece soporte para JSONP, de forma que nos resultará muy sencillo obtener estos mismos resultados de manera automatizada y usarlos en nuestras propias páginas.

Así, por ejemplo, para obtener en formato RSS los enlaces que la gente ha clasificado bajo la etiqueta "ASP.NET" escribiríamos:

http://feeds.delicious.com/v2/RSS/popular/ASP.NET

Y en nuestro caso, para obtener lo mismo en formato JSON necesitaríamos una llamada similar a esta:

http://feeds.delicious.com/v2/JSON/popular/ASP.NET?callback=Procesar

Aunque en la documentación de delicio.us no aparecen documentados los campos del JSON devuelto, podemos interceptarlos fácilmente con Fiddler o una herramienta similar, y luego usar un visor de JSON (este del enlace es alucinante) para ver fácilmente su estructura de datos:

Como podemos observar la estructura es muy sencilla y devuelve una matriz de objetos con los siguientes campos de datos:

  • u: la URL del favorito
  • d: la descripción del mismo
  • t: una matriz de tags asociadas con la URL
  • dt: la fecha en la que se guardo el favorito, con este formato: "yyyy-MM-ddTHH:mm:ssZ".

Por lo tanto sacarle partido directo desde una página HTML es realmente sencillo usando las nuevas capacidades de Live Binding de datos con JavaScript que vienen con ASP.NET AJAX 4.0.

Puedes descargarte la versión más reciente del ASP.NET AJAX Preview (lo que viene en el futuro pero ya está disponible en pruebas, que ahora va por su release 5) desde CodePlex. Dentro de la carpeta de ejemplos se incluye uno hecho con JSONP. Una versión ligeramente retocada del mismo la he colgado en este Blog para que puedas jugar con ella y es la que vamos a analizar brevemente. En el siguiente marco interna puedes verla en funcionamiento y probarla, cargando por defecto los enlaces populares para ASP.NET:

Se trata de una página HTML pura y dura. No hay código de servidor alguno. Puedes descargarte el código en este archivo ZIP. No obstante lo tienes, como ya he dicho, entre los ejemplos de ASP.NET AJAX Preview.

¿Cómo funciona esto?

En este ejemplo, para hacer las llamadas, se crea un objeto para representar una etiqueta <script> que se hace apuntar a la URL apropiada de Delicio-us que devuelve el tag que nos interesa, tal y como ya hemos visto:

http://feeds.delicious.com/v2/JSON/popular/ASP.NET?callback=queryComplete

En este caso nuestra función de JavaScript para procesar los resultados se llama queryComplete.

Su código es simplemente este:

        function queryComplete(results) {
            $find("dataView").set_data(results);
        }

"dataView" es el nombre de un div de la página al que se le ha añadido el comportamiento de enlazado a datos gracias al control de lado cliente DataView de ASP.NET AJAX 4.0. El formato de la plantilla de enlazado a datos (Live Binding) de lado cliente es este:

        <div id="dataView" sys:attach="dv" class="sys-template">
            <p class="linkItem">
                <p class="linkTitle">
                    <span class="title">
                        <a sys:href="{{ u }}" target="delicious">{{ d }}</a>
                    </span>
                    <span class="href">
                        <a sys:href="{{ u }}" target="delicious">{{ u }}</a>
                    </span>
                </p>
                
                <!--Lista anidada de Tags-->
                <span class="taglist sys-template" 
                    sys:attach="dv" 
                    dv:data="{{ t }}" 
                    dv:oncommand="{{ onTaglistCommand }}"
                >
                    <span class="tag" sys:command="viewtag" sys:commandargument="{{ $dataItem }}" >
                        {{ $dataItem }}
                    </span> |
                </span>
                
                <span class="date">
                    {{ Date.parseInvariant(dt, "yyyy-MM-ddTHH:mm:ssZ")
                    .localeFormat("@ dd/MM/yyyy HH:mm:ss") }}
                </span>
            </p>
        </div>

Si necesitas conocer cómo funciona el Live Binding en ASP.NET 4.0 te recomiendo que lo leas en mi libro "Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)", que tienes disponible para lectura on-line en el enlace anterior y para compra en papel.

Básicamente se puede ver que lo que hace el marcado anterior es enlazar los campos "u" y "d" del JSON devuelto para formar dos enlaces, utiliza un DataView anidado para mostrar las diferentes tags, que se convierten en comandos para lanzar subsiguientes consultas de filtrado por tag. Finalmente usa la clase Date para dar un formato más apropiado a la fecha y enlazarla a un span informativo final.

Para realizar las llamadas se usa una función queryPopularLinksForTag que, en última instancia lo que hace es usar una función sendJsonRequest que tiene el siguiente aspecto:

        function sendJsonpRequest(url, callback) {
            var head = document.getElementsByTagName("head")[0];
            if (queryScript) {
                head.removeChild(queryScript);
            }
            queryScript = document.createElement("script");
            // JSONP URL has query parameter specifying name of callback function  
            queryScript.src = url + "?callback=" + callback;
            head.appendChild(queryScript);
        }

Lo que se hace es crear un nuevo objeto para representar una etiqueta <script> (document.createElement("script")) al cual se le asigna en su propiedad src la URL de delicio.us que nos devolverá el JSON envuelto en una llamada a la función de callback (en este caso queryComplete).

Como vemos es realmente sencillo y nos abre todo un mundo de posibilidades.

En un nuevo post explicaré los peligros de seguridad de JSONP y porqué, debido a ellos, los servicios Web expuestos como JSON en ASP.NET cambiaron la forma de devolver los datos (y son incompatibles con los de la versión anterior).

¡Hasta pronto!

José Manuel Alarcón
Banner

Agregar comentario