November 08, 2004

Integración de Web Services con Flash. Parte 2 de 3.

En el capitulo anterior de este articulo comentábamos que el uso de los Web Services con Flash planteaban una serie de problemas. El mas grave de todos es la necesidad de usar un archivo Policy File para poder habilitar el swf que cargara el Web Service. Como funciona esto del Policy File?

p>De la siguiente manera:

Usualmente un sitio esta estructurado de tal forma que todo lo que llegásemos a requerir se encuentre dentro de nuestro document root en el servidor, y para poder acceder a los archivos del mismo se puede usar tanto rutas absolutas (comenzando desde la raíz del dominio) o rutas relativas (a partir del documento que estamos invocando). Adicionalmente hay sitios que te dan la alternativa de usar la ruta corta (sin el www) o habilitar subdominios en el caso de que usemos rutas absolutas. Cuando una película flash trate de cargar cualquier cosa que se encuentre en otro servidor o que la ruta no sea exactamente igual a la del swf madre (incluido subdominios y rutas cortas) es posible que te aparezca una alerta o bien, que no te cargue el pedido. Para solucionar tal cosa se necesita crear un archivo crossdomain.xml dentro del document root del servidor externo o el subdominio, donde Flash, al intentar buscar un pedido en otro servidor, primero antes de todo tratara de leer este archivo y checará que se tengan los permisos para poder realizar la carga, Si no lo encuentra como es debido, no funcionara el código o aparecerá un mensaje de alerta muy molesto. Como opinión personal creo que esta implementación de seguridad es algo excesiva y poco practica, mas cuando queremos tratar de usar algún Web Service que en teoría son públicos y las restricciones de seguridad deberían ir mas bien en la implementación del servicio. Mas información acerca del Policy File la puedes encontrar en este interesante y breve articulo de Collin Moock (en ingles).



El segundo problema como bien ya lo comentamos en la Action anterior, se refiere al parseo de datos de Flash…convertir el XML resultante del servicio a objetos nativos en actionscript. Si la información que devuelve el Web Service tiene un tamaño considerable, el Flash Player se puede pasmar, y de hecho cargar un XML siempre ha sido pesado para Actionscript, mas aun ahora que se tiene que reparsear la información y convertirla.

El ultimo problema, del que no hemos hablado propiamente, es que en ocasiones puede que las llamadas no funcionen o se reciba un error dentro, debido a que Actionscript tiene un soporte limitado para trabajar con Web Services. Un articulo interesante nos lo ofrece Vera Fleischer en la pagina de Macromedia.

Ahora si, como en todo problema, se deben buscar soluciones. Para el primer caso se puede resolver creando un Proxy con algún lenguaje como PHP, ASP o ColdFusion y mezclado con objetos LoadVars o XML del lado de Flash, y pero a su vez esta solución provoca una nueva dificultad, ya que la información resultante y procesada en el servidor se recibe a manera de cadenas URL o en un XML, perdiendo el plus de los Web Services de poder usar objetos nativos de Flash (aunque Actionscript tuviera que reparsearlos internamente), principalmente cuando hablamos de los Arrays que tanta flexibilidad nos proporcionan para estructurar datos. Para solventarlo (aunque no es el ideal) podemos usar la clase Serializer de Alssandro Sephiroth que permite serializar / deserializar datos entre Actionscript y PHP (y otra vez vendría el reparseo del lado de Flash). Para este ejercicio no usaremos la clase Serializer, pero posteriormente se ofrecerá un breve articulo del mismo. Continuando con la solución de problemas, sea el caso del ultimo, utilizaremos la librería NuSoap de PHP, el cual tiene mejor soporte para los WSDL, para traducir la información del Web Service a algo que entienda Flash.

Para resumir, este método que utilizaremos mediante un archivo que hará de proxy es perfecto para solventar la primer primera dificultad (Policy File) y la tercera (soporte incompleto de Flash para Web Services). Para el segundo se puede usar alternativamente la ya comentada clase Serializer, aunque no es realmente necesaria. La técnica ideal para resolver completamente todos los problemas es usando Flash Remoting, el cual veremos en la ultima serie de estos artículos.

Ahora si, hagamos un listado de lo que necesitamos.

1.- Macromedia Flash MX Profesional 2004
2.- Un servidor con PHP (por ejemplo, Apache)
3.- La librería NuSoap.


Comencemos con el código PHP:

&amp;lt;?php<br />$texto = $_POST['texto'];<br />$cantidad = $_POST['cantidad'];<br />require_once('nusoap.php');<br /><br />$parametros = array(<br /> &amp;quot;SearchString&amp;quot; =&amp;gt;$texto,<br /> &amp;quot;NumResults&amp;quot; =&amp;gt;$cantidad<br />);<br /><br />$cliente = new soapclient ('http://www.agnisoft.com/cgi-bin/soapmp3search.exe/wsdl/ISoapFindMP3' ,true, &amp;quot;ISoapFindMP3Port&amp;quot;);<br />$resultado = $cliente-&amp;gt;call('SearchMP3',$parametros);<br /><br />$xml = &amp;quot;&amp;lt;busqueda texto='&amp;quot;.$texto.&amp;quot;' cantidad='&amp;quot;.$cantidad.&amp;quot;'&amp;gt;&amp;quot;;<br />$return = $resultado['return'];<br />for ($i=0; $i&amp;lt;count($return); $i++) {<br /> $xml .= &amp;quot;&amp;lt;item&amp;gt;&amp;lt;url&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['URL'].&amp;quot;]]&amp;gt;&amp;lt;/url&amp;gt; &amp;lt;filename&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['FileName'].&amp;quot;]]&amp;gt;&amp;lt;/filename&amp;gt;&amp;quot;.&amp;quot;<br /> &amp;lt;size&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['Size'].&amp;quot;]]&amp;gt;&amp;lt;/size&amp;gt;<br /> &amp;lt;speed&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['Speed'].&amp;quot;]]&amp;gt;&amp;lt;/speed&amp;gt;<br /> &amp;lt;/item&amp;gt;&amp;quot;;<br />}<br />$xml .= &amp;quot;&amp;lt;/busqueda&amp;gt;&amp;quot;;<br /><br />header (&amp;quot;Content-type:text/xml&amp;quot;);<br />echo utf8_encode ($xml);<br />?&amp;gt;


Es importante tener la librería nusoap.php al lado de tu archivo proxy.php para que podamos usar la clase soapclient. Veamos la primera parte del código de proxy.php :

$texto = $_POST['texto'];<br />$cantidad = $_POST['cantidad'];<br />require_once('nusoap.php');


Almacenamos las variables que recibimos de Flash mediante POST e incluimos la librería nusoap.php.

$parametros = array(<br /> &amp;quot;SearchString&amp;quot; =&amp;gt;$texto,<br /> &amp;quot;NumResults&amp;quot; =&amp;gt;$cantidad<br />);<br />$cliente = new soapclient ('http://www.agnisoft.com/cgi-bin/soapmp3search.exe/wsdl/ISoapFindMP3' ,true);<br />$resultado = $cliente-&amp;gt;call('SearchMP3',$parametros);


Creamos un array asociativo que contendrá los argumentos tal y como están definidos en el servicio y sus valores. En la siguiente linea creamos un objeto soapclient, donde su primer argumento es la URL del servicio y en el segundo activamos la posibilidad de usar los WSDL. Ya con esto invocamos el método SearchMP3 y le agregamos el Array $parametros. La respuesta al servicio la almacenamos dentro de $resultado. Dependiendo que datos nos devuelva agnisoft, será la manera en que trabajemos con el mismo. En este caso es un array asociativo con dos propiedades, “return” y “!SOAP-ENV:encodingStyle” Dentro de return (la propiedad que realmente nos interesa) hay otro Array que contiene todos nuestros resultados ordenados por indice numérico. Mediante un for accedemos a cada uno de los elementos y lo transformamos en un XML:

for ($i=0; $i&amp;lt;count($return); $i++) {<br /> $xml .= &amp;quot;&amp;lt;item&amp;gt;&amp;lt;url&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['URL'].&amp;quot;]]&amp;gt;&amp;lt;/url&amp;gt;<br />&amp;lt;filename&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['FileName'].&amp;quot;]]&amp;gt;&amp;lt;/filename&amp;gt;&amp;quot;.<br /> &amp;quot;&amp;lt;size&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['Size'].&amp;quot;]]&amp;gt;&amp;lt;/size&amp;gt;<br />&amp;lt;speed&amp;gt;&amp;lt;![CDATA[&amp;quot;.$return[$i]['Speed'].&amp;quot;]]&amp;gt;&amp;lt;/speed&amp;gt;&amp;lt;/item&amp;gt;&amp;quot;;<br />} <br />$xml .= &amp;quot;&amp;lt;/busqueda&amp;gt;&amp;quot;;


Hay que fijarnos muy bien en la estructura del XML a imprimir, porque esta será la que leeremos desde Flash. Cada nodo tendrá 4 subnodos de tipo CDATA (es mas seguro usarlo, ya que no sabemos realmente que nos pueda devolver el servicio) que contendrán la información de cada Mp3.

header (&amp;quot;Content-type:text/xml&amp;quot;);<br />echo utf8_encode ($xml);


Aquí mandamos un header al navegador declarando el XML, aunque realmente a Flash no le importa si lo enviamos o no…pero es bueno ponerlo ahí si queremos desplegar el XML en un navegador y nos valide si esta correcto o no, ademas de colorearlo. En la ultima linea mostramos con formato UTF-8 el resultado que es el enconding que maneja Flash desde la versión MX.

Ahora veamos el código de Actionscript:

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 datosLV:LoadVars = new LoadVars();<br />var datosXML:XML = new XML();<br /><br />var oBotonServicio:Object = new Object();<br />var oListaMP3:Object = new Object();<br /><br />oBotonServicio.click = function() {<br /> listaDes.removeAll()<br /> campoInfo.text = &amp;quot;&amp;quot;;<br /> datosLV.texto = campoNombreMP3.text;<br /> datosLV.cantidad = 10;<br /> datosLV.sendAndLoad(&amp;quot;http://localhost/webservices/proxy.php&amp;quot;<br />, datosXML)<br />};<br />function resultadoInfo(ok):Void {<br /> if (ok) {<br /> var nodos:XMLNode = this.firstChild.childNodes;<br /> var largo:Number =this.firstChild.childNodes.length;
for (var i = 0; i&amp;lt;largo; i++) {<br /> var mp3:String = nodos[i].childNodes[1].firstChild.nodeValue;<br /> var pos:Number = mp3.toLowerCase().lastIndexOf(&amp;quot;.mp3&amp;quot;) != -1 ? mp3.toLowerCase().lastIndexOf(&amp;quot;.mp3&amp;quot;) : mp3.length;<br /> var label:String = mp3.substr(0, pos);<br /> var data:Object = { url:nodos[i].childNodes[0].firstChild.nodeValue, <br /> tamano:nodos[i].childNodes[2].firstChild.nodeValue, <br /> velocidad:nodos[i].childNodes[3].firstChild.nodeValue<br /> };<br /> listaDes.addItem(label, data);<br /> }<br /> } else {<br /> listaDes.addItem(&amp;quot;Hubo un error en el servidor, intente de mas tarde&amp;quot;, &amp;quot;&amp;quot;);<br /> }<br />}<br />oListaMP3.change = function() {<br /> if (listaDes.selectedItem.data instanceof Object) {<br /> var obj:Object = listaDes.selectedItem.data;<br /> campoInfo.text = &amp;quot;peso del archivo : &amp;quot;+Number(obj.tamano)/1000+&amp;quot; Kb\n&amp;quot;+&amp;quot;velocidad : &amp;quot;+obj.velocidad;<br /> }<br />};<br /><br />datosXML.onLoad = resultadoInfo<br />listaDes.addEventListener(&amp;quot;change&amp;quot;, oListaMP3);<br />botonServ.addEventListener(&amp;quot;click&amp;quot;, oBotonServicio);


La estructura es muy similar al ejercicio anterior donde conectamos directamente el Web Service con Flash.

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 datosLV:LoadVars = new LoadVars();<br />var datosXML:XML = new XML();<br /><br />var oBotonServicio:Object = new Object();<br />var oListaMP3:Object = new Object();


Ademas de la declaración del Data Typing de los componentes, creamos tanto un objeto LoadVars para el envío de variables al archivo proxy.php vía POST como un objeto XML para recibir el resultado tal como lo imprimimos en el PHP. Las ultimas dos lineas son nuestros listeners que escucharan por los eventos de los componentes.

oBotonServicio.click = function() {<br /> listaDes.removeAll()<br /> campoInfo.text = &amp;quot;&amp;quot;;<br /> datosLV.texto = campoNombreMP3.text;<br /> datosLV.cantidad = 10;<br /> datosLV.sendAndLoad(&amp;quot;http://localhost/webservices/proxy.php&amp;quot;, datosXML)<br />};


Lo siguiente es definir que hará exactamente nuestro botón de búsqueda. De hecho este código es similar al del botón del ejemplo anterior de Web Services donde borrábamos los elementos del List, se limpiaba campoInfo y enviábamos las variables al servidor, solo que aquí lo hacemos a través de un LoadVars. Si bien recordamos, podemos crear propiedades dinámicas que serán las variables a que recogerá el PHP. Ademas nosotros esperamos una respuesta del servidor cuando termine de conectarse al Web Service y procese la información, para finalmente mostrarla a Flash con un XML. Ahí entra nuestro método sendAndLoad…. envía y carga: el primer parámetro es la URL del archivo proxy.php y el segundo es el objeto que recibirá la respuesta. Aquí usamos un truco al que se suele recurrir, dado a que el parseo de XML en PHP es algo confuso, difícil y se debe tener activado en nuestro servidor la variable global $HTTP_POST_RAW_DATA (no siempre tenemos acceso al php.ini). Otro detalle a considerar es que se debe cambiar el contentType del XML a otro que no sea “application/x-www.form-urlencoded” (que es la que tiene por default) para poderla recibir en el PHP. Para facilitarnos las cosas, usamos LoadVars.

function resultadoInfo(ok):Void {<br /> if (ok) {<br /> var nodos:XMLNode = this.firstChild.childNodes;<br /> var largo:Number=this.firstChild.childNodes.length;
for (var i = 0; i&amp;lt;largo; i++) {<br /> var mp3:String = nodos[i].childNodes[1].firstChild.nodeValue;<br /> var pos:Number = mp3.toLowerCase().lastIndexOf(&amp;quot;.mp3&amp;quot;) != -1 ? mp3.toLowerCase().lastIndexOf(&amp;quot;.mp3&amp;quot;) : mp3.length;<br /> var label:String = mp3.substr(0, pos);<br /> var data:Object = { url:nodos[i].childNodes[0].firstChild.nodeValue, <br /> tamano:nodos[i].childNodes[2].firstChild.nodeValue, <br /> velocidad:nodos[i].childNodes[3].firstChild.nodeValue<br /> };<br /> listaDes.addItem(label, data);<br /> }<br /> } else {<br /> listaDes.addItem(&amp;quot;Hubo un error en el servidor, intente de mas tarde&amp;quot;, &amp;quot;&amp;quot;);<br /> }
}


Creamos el callback onLoad del objeto XML . Este procesa y valida que la información este como se debe, desplegando un mensaje en el List. Dentro del if viene el cuerpo del código, la parte interesante, donde creamos una variable nodos (y le damos el Data Typing de XMLNode, ya que asi están formados los XML en realidad) y ciclamos dentro de el como si fuera el Array que nos devolvía el Web Service directo, como lo vimos en el articulo anterior. El resto de las lineas es prácticamente lo mismo ya que la estructura de los nodos es igual. Posteriormente obtenemos el nombre del archivo sin la extensión, creamos un objeto genérico y agregamos los elementos dentro del List con addItem().

oListaMP3.change = function() {<br /> if (listaDes.selectedItem.data instanceof Object) {<br /> var obj:Object = listaDes.selectedItem.data;<br /> campoInfo.text = &amp;quot;peso del archivo : &amp;quot;+Number(obj.tamano)/1000+&amp;quot; Kb\n&amp;quot;+&amp;quot;velocidad : &amp;quot;+obj.velocidad;<br /> }<br />};<br /><br />datosXML.onLoad = resultadoInfo;<br />listaDes.addEventListener(&amp;quot;change&amp;quot;, oListaMP3);<br />botonServ.addEventListener(&amp;quot;click&amp;quot;, oBotonServicio);


Se crea el evento change a oListaMP3 ademas de asignar la función resultadoInfo al onLoad de datosXML. Por ultimo, registramos los listeners.

Con todo esto, ya es posible publicar la película Flash y visualizarla desde un servidor (y/o navegador) sin los problemas del Policy File, y sea para recordar a manera de pasar un momentáneo mal rato, sigo sin entender porque Macromedia implemento esta medida de seguridad… si se puede acceder al Web Service a través del lenguaje de servidor, pues da lo mismo, son públicos… solo es para complicar mas la vida de uno. Nuestra aplicación ahora si debe estar funcionando correctamente. En el siguiente articulo veremos la integración de Flash Remoting con Web Services, la mejor solución de las tres que estamos viendo para utilizar estas útiles tecnologías.

Muchas gracias por su atención.

Archivos Fuentes

Actualización  20/Ago/2005 : He eliminado una parte donde se hablaba de editar el codigo fuente del nusoap. He testeado de nuevo la aplicacion con la ultima version del nusoap y parece estar solucionado el problema del encoding.

Comentarios

el link a los archivos fuente parece no estar bien

de momento estoy tratando de hacerlo yo mismo, muy interesante porcierto

Ya lo arregle, parece que a Daniel se le chispotio el pat :)

ouch!!!!

Glad you liked my article! :)

of course, I think your article is great and help me so much when I was looking for info :)

have a nice day!

Las dos ayudas me han servido muchisimo pero ahun asi me gustaria conocer como se hace con flash remoting pero no lo he encontrado en tu sitio. Además en estos días conocí flex y he trabajado con él, pero me gustaría hacer aplicaciones mas dinamicas conectandolo con java o .net pero no he encontrado mucha ayuda. Si tuvieras algo que me sirviera te lo agradecería

Perfecto para mi, Gracie !!





Leave this field empty: