Integración de Web Services con Flash – Parte 1 de 3
Flash desde su versión MX 2004 Pro ya trae unas clases que permiten la integración directa con algo llamado Web Services. Para versiones anteriores se tenia que usar un archivo puente ya sea de PHP, ASP o similar que leyera el Web Services y lo tradujera a cadenas URL o en un XML. Para aquellos que no sepan que son los Web Services, les puedo comentar que son archivos XML que usualmente están alojados en servidores externos y te permiten acceder a ciertas funcionalidades muy útiles. Un listado muy completo es el de xmethods. Por ejemplo, Google ofrece unos Web Services que donde envías una palabra y te regresa el resultado de la búsqueda tal y como lo escribiésemos directamente en su sitio. Ya con esta información cargada nosotros podemos mostrarla en Flash de una manera interesante e interactiva :) . Otros Web Services muy famosos son BabelFish o el de Weather Channel los cuales sirven para traducciones y saber el estado del tiempo respectivamente.
p>Veamos mas a fondo la estructura de un Web Service: usualmente están construidos en PHP, ASP, ColdFusion, C++, Java… de hecho el lenguaje no importa, ya que utilizan formatos estándares (aunque todavía la W3 no los ha aprobado) a base de librerías SOAP que facilitan el envío y recepción de mensajes entre diversas tecnologías. Los Web Services los podemos visualizar mediante archivos WSDL (Web Services Description Language) los cuales son XML que describen como están construidos, los métodos que se pueden invocar, sus argumentos y un par de cosas mas de las cuales no profundizaremos. Un excelente y breve tutorial para comprender como esta estructurado un WSDL lo pueden encontrar aquí en w3schools. Nosotros mas bien nos enfocaremos a como se integran con Flash. Estos artículos se dividirán en tres partes. La primera se enfocara a usar los Web Services directamente desde Flash. En la segunda parte veremos un método alterno donde un archivo hará de proxy / puente para hacer las llamadas a los Web Services. Esto es debido a que el Flash Player tiene unas restricciones de seguridad que tienen que ver con el Policy File. Mas adelante, en el segundo articulo veremos a que nos referimos. La ultima parte se enfocara a el uso de Web Services con Flash Remoting :) . Para terminar, en este articulo, usaremos la clase WebService que nos da mas control sobre la aplicación. Se puede usar el componente WebServiceConnector, pero ademas de que perderíamos flexibilidad, se hace mas pesado el swf.Comencemos:
Si nos vamos a Windows -> Development Panels -> Web Services encontraremos una ventana donde podemos visualizar nuestros servicios. Por que nos es útil esta ventana? Porque si vemos el documento WSDL directamente probablemente nos será muy difícil de entender. Clickeamos sobre un icono que parece un mundo, agregamos la url de nuestro Web Service y le damos Ok. Cuando Flash termine de cargar el servicio podremos acceder a un listado de métodos y a su vez, cada método tendrá dos listas. Una donde nos indica que tipo de dato devuelve, y la otra donde podremos ver que argumentos necesita y el tipo de dato requerido. Es muy importante checar esta información, ya que si no invocamos nuestro servicio correctamente, no funcionara. Para este tutorial usaremos el Web Service que nos provee agnisoft para hacer búsquedas de mp3 en internet (y como es por motivos educacionales, no nos puede demandar la RIAA :P ). El WSDL lo encontramos aquí, y como bien comentamos, es un documento bastante difícil de leer. Una vez agregado a nuestra ventana de Web Services, veremos que nos provee solamente de un método llamado searchMp3 el cual acepta dos argumentos: un String indicando lo que se buscara, y un Number que será la cantidad máxima de resultados a devolver. Ademas podremos ver que el método nos regresa un Array de objetos TMP3 que en realidad son XMLNodes (aquí es donde nos podemos preguntar porque simplemente no nos devuelve un XML, si a final de cuentas prácticamente eso es…un array de XMLNodes) . Veamos el código completo de Actionscript:
import mx.services.*;<br />var botonServ:mx.controls.Button;<br />var campoNombreMP3:mx.controls.TextArea;<br />var campoInfo:mx.controls.TextArea;<br />var listaDes:mx.controls.List;<br /><br />var log:Log = new Log(Log.VERBOSE);<br />log.onLog = function(texto) {<br /> trace(texto);<br />};<br /><br />var servicio:WebService = new WebService (&quot;http://www.agnisoft.com/cgi-bin/soapmp3search.exe/wsdl/ISoapFindMP3&quot;, log);<br /><br />var oBotonServicio:Object = new Object();<br />var oListaMP3:Object = new Object();<br />var oBotonDescarga:Object = new Object();<br />var resultado:Object;<br /><br />oBotonServicio.click = function() {<br /> listaDes.removeAll();<br /> campoInfo.text = &quot;&quot;;<br /> var t:String = campoNombreMP3.text;<br /> resultado = servicio.SearchMP3(t, 10);<br /> resultado.onResult = resultadoInfo;<br />};<br /><br />function resultadoInfo(info):Void {<br /> if (info.length != 0) {<br /> for (var i = 0; i&lt;info.length; i++) {<br /> var mp3:String = info[i].childNodes[1].firstChild.nodeValue;<br /> var pos:Number = mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) != -1 ? mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) : mp3.length;<br /> var label:String = mp3.substr(0, pos);<br /> var data:Object = { url:info[i].childNodes[0].firstChild.nodeValue, <br /> tamano:info[i].childNodes[2].firstChild.nodeValue, <br /> velocidad:info[i].childNodes[3].firstChild.nodeValue<br /> };<br /> listaDes.addItem(label, data);<br /> }<br /> } <br /> else {<br /> listaDes.addItem(&quot;No hubo resultados en su busqueda, intente de nuevo&quot;, &quot;&quot;);<br /> }<br />}<br /><br />oListaMP3.change = function() {&nbsp;<br /> var obj:Object = listaDes.selectedItem.data;<br /> campoInfo.text = &quot;peso del archivo : &quot;+Number(obj.tamano)/1000+&quot; Kb\n&quot;+&quot;velocidad : &quot;+obj.velocidad;<br />};<br /><br />listaDes.addEventListener(&quot;change&quot;, oListaMP3);<br />botonServ.addEventListener(&quot;click&quot;, oBotonServicio);
Que necesitaremos para este tutorial? Lo primero es importar las librerías de Web Services de Flash. Nos vamos a Windows -> Other Panels -> Common Libraries -> Classes y se abrirá una ventana. Arrastraremos la librería de WebServicesClasses y ahí es donde entra la primera linea:
import mx.services.*;
Aquí es donde importamos las librerías necesarias para hacer que funcionen los Web Services. Ya que este en nuestro escenario el movieclip compilado WebServicesClasses lo podemos borrar, lo importante es que este se encuentre en la librería de la película.
var botonServ:mx.controls.Button;<br />var campoNombreMP3:mx.controls.TextArea;<br />var campoInfo:mx.controls.TextArea;<br />var listaDes:mx.controls.List;
Declaración del Data Tiping de los componentes que usaremos. Necesitamos un TextArea donde ingresaremos la palabra a buscar, un Button que hará la llamada, un List donde aparecerá el resultado de la búsqueda y otro TextArea donde se mostrara información adicional del mp3.
var log:Log = new Log(Log.VERBOSE);<br />log.onLog = function(texto) {<br /> trace(texto);<br />};
Creamos un objeto Log que sirve para ir mostrando el estado del servicio. Cada vez que suceda algo (con el evento onLog), aparecerá un mensaje en la ventana de Output. Mas información del objeto Log aquí.
var servicio:WebService = new WebService (&quot;http://www.agnisoft.com/cgi-bin/soapmp3search.exe/wsdl/ISoapFindMP3&quot;, log);
La clase WebService dentro de su función constructora acepta un argumento obligatorio, que es la URL del servicio, y uno opcional, que es donde ingresamos el objeto Log que checará el estado del servicio. Ya creado nuestro WebService, podemos invocar los métodos directamente.
var oBotonServicio:Object = new Object();<br />var oListaMP3:Object = new Object();<br />var resultado:Object;
Para el funcionamiento de nuestra aplicación necesitamos algunos objetos que escucharan por los eventos. oBotonServicio es un listener para el botón que invocara la búsqueda. oListaMP3 contendrá el evento para saber cuando se hace click sobre algún elemento de la lista. Por ultimo, el objeto resultado es aquel que tendrá el event handler para recibir la respuesta del Web Service (ahora veremos porque se necesita este último).
oBotonServicio.click = function() {<br /> listaDes.removeAll()campoInfo.text = &quot;&quot;;<br /> var t:String = campoNombreMP3.text;<br /> resultado = servicio.SearchMP3(t, 10);<br /> resultado.onResult = resultadoInfo;<br />}; Definimos el evento click para nuestro oListaMp3 (en las ultimas lineas registraremos los objetos). Que sucede dentro? Borramos los elementos del componente List por si antes hubo una búsqueda, ademas de que limpiamos el TextArea que muestra la información adicional del Mp3. En la siguiente linea creamos una variable temporal llamada t que contendrá el String que será la llave de nuestra petición. Desde nuestro objeto servicio invocamos directamente el método del Web Service como si le perteneciera, y dentro de los argumentos ponemos la variable t y un 10, para limitar la cantidad de elementos a devolver en la búsqueda. Por la forma en que esta construida la clase WebService cada vez que invoquemos un método, nos devolverá un objeto que tendrá la capacidad de escuchar por la respuesta al servicio mediante el evento onResult. En la ultima linea solo asignamos al evento la función resultadoInfo que es lo que veremos a continuación:
function resultadoInfo(info):Void {<br /> if (info.length != 0) {<br /> for (var i = 0; i&lt;info.length; i++) {<br /> var mp3:String = info[i].childNodes[1].firstChild.nodeValue;<br /> var pos:Number = mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) != -1 ? mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) : mp3.length;<br /> var label:String = mp3.substr(0, pos);<br />var data:Object = {<br /> url:info[i].childNodes[0].firstChild.nodeValue, <br /> tamano:info[i].childNodes[2].firstChild.nodeValue, <br /> velocidad:info[i].childNodes[3].firstChild.nodeValue<br /> };<br />listaDes.addItem(label, data);<br />}<br /> } <br /> else {<br /> listaDes.addItem(&quot;No hubo resultados en su busqueda, intente de nuevo&quot;, &quot;&quot;);<br />}<br />} Esta es la parte importante de nuestro codigo…la manera en que vamos a recibir el servicio. Es importante especificar un argumento dentro de la definicion del event handler ya que esta sera la variable que contendra el dato que devuelve el servicio. Esta manera de manejar argumentos dentro de eventos es parecida al onLoad de los objetos LoadVars para validar si la cadena URL que fue cargada es valida o no. En el caso de este servicio en especifico, como bien vimos en nuestra ventana de Web Services, nos devuelve un Array de XMLNodes. Si el Array tiene un length de 0 signfica que no hubo resultados, en caso contrario podremos a los XMLNodes mediante un ciclo for que busca sobre cada elemento en el arreglo. La manera en que los nodos estan estructurados es la siguiente:
&lt;item xsi:type=&quot;NS2:MP3Result&quot;&gt;<br /> &lt;URL xsi:type=&quot;xsd:string&quot;&gt; URL del Mp3 &lt;/URL&gt;<br /> &lt;FileName xsi:type=&quot;xsd:string&quot;&gt; Nombre del archivo &lt;/FileName&gt;<br /> &lt;Size xsi:type=&quot;xsd:string&quot;&gt; Peso del archivo &lt;/Size&gt;<br /> &lt;Speed xsi:type=&quot;xsd:string&quot;&gt; Velocidad de Transferencia &lt;/Speed&gt;<br />&lt;/item&gt; Siguiendo la lógica de los XMLNodes el primer nodo seria info[i] y los que contiene se accederían mediante nodo[i].childNodes[numero del nodo]. El primero de los subnodos seria <URL>, el segundo <FileName>, después <Size> y al final <Speed>. Veamos estas lineas dentro del for:
var mp3:String = info[i].childNodes[1].firstChild.nodeValue;<br />var pos:Number = mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) != -1 ? mp3.toLowerCase().lastIndexOf(&quot;.mp3&quot;) : mp3.length;<br />var label:String = mp3.substr(0, pos);En la variable mp3 almacenamos la infamación del nodo <FileName>. La idea es quitarle la extensión .mp3, en caso de que lo tenga, para que quede el puro nombre. Con la variable pos vemos en que posición esta el String “.mp3” dentro de mp3 y si no hay tal cosa, que devuelva el puro largo del String. En la siguiente linea almacenamos el resultado del “rebanado” en la variable label.
var data:Object = {url:info[i].childNodes[0].firstChild.nodeValue,<br />tamano:info[i].childNodes[2].firstChild.nodeValue, <br /> velocidad:info[i].childNodes[3].firstChild.nodeValue<br /> };
Dentro de data creamos un objeto que contendrá las propiedades url, tamano y velocidad cuyos valores los sacaremos directamente de los nodos y utilizaremos para desplegarlos dentro de el campo de texto.
listaDes.addItem(label, data);
Agregamos la información dentro de nuestro componente List. En caso de que el length de info sea 0, no se ejecutara el ciclo y aparecerá "No hubo resultados en su búsqueda, intente de nuevo" en el List.
oListaMP3.change = function() {<br />var obj:Object = listaDes.selectedItem.data;<br /> campoInfo.text = &quot;peso del archivo : &quot;+Number(obj.tamano)/1000+&quot; Kb\n&quot;+&quot;velocidad : &quot;+obj.velocidad;<br /> };
Definimos el evento change para nuestro listener oListaMp3 que escuchará cuando se haga click sobre un elemento del componente List. Lo único que hará es mostrar el peso del archivo en Kb y la velocidad de transferencia del mismo. En caso de que el servicio no sepa que velocidad de transferencia es, aparecerá un N/A (Not Avaible).
listaDes.addEventListener(&quot;change&quot;, oListaMP3);<br />botonServ.addEventListener(&quot;click&quot;, oBotonServicio);Para terminar solamente registramos los listeners y sus respectivos eventos dentro de los componentes listDes y botonServ. Ahora si…publiquemos nuestro swf y veremos la aplicación funcionando. Como apunte final, mencionaremos que este ejemplo solo funcionara si se corre desde el desktop, y no en el navegador, por las restricciones de seguridad del Flash Player, de las cuales hablaremos mas detalladamente en la siguiente parte de esta serie de artículos. Este es uno de los problemas graves con Flash... porque en teoría si alguien desarrolla un Web Service es para que sea publico y no tengamos que pedirle permiso al dueño del mismo para que suba el Policy File y nos deje acceder a su WSDL desde Flash. El truco para solventar esta dificultad lo veremos en la siguiente parte de estos artículos. Otro problema, es que la información se recibe a manera de XML y Flash tiene que reparsearla para convertirla a objetos nativos que pueda usar. Si el resultado es un objeto muy grande y complejo, puede pasmar al Flash Player. Esto se resuelve mediante Flash Remoting (tercera parte de los artículos) ya que la información se recibe comprimida y no se tiene que convertir a objetos nativos, porque ya lo están.
Es todo por ahora, muchas gracias por su atención.
Comentarios
**Error** Escena=Escena 1, capa=Capa 1, fotograma=1:Línea 6: No se ha podido cargar la clase 'Log'.
var log:Log = new Log(Log.VERBOSE);
**Error** Escena=Escena 1, capa=Capa 1, fotograma=1:Línea 11: No se ha podido cargar la clase 'WebService'.
var servicio:WebService = new WebService ("http://www.agnisoft.com/cgi-bin/soapmp3search.exe/wsdl/ISoapFindMP3", log);
Total de errores de ActionScript: 2 Errores comunicados: 2
esos son los errores
Si me pueden ayudar se los agradezco