FMS 调用 WEBSERVICE(2)
?RequirementsPrerequisite knowledgeYou should be familiar with Macromedia Flash from Adobe and understand the basics of ActionScript in both Flash and Flash Media Server 2. It would also help to have a basic understanding of XML. User levelIntermediate Required products
Sample files
The design goal of any web service is to allow both developers and companies to share data easily. What makes this possible is the ability to output information in standard XML format so that programs written in different languages can communicate with the same service. You can also use your favorite language and platform to interface with Macromedia Flash Media Server 2 from Adobe. Web services on the server side allow you to interact with your production servers without ever exposing the connection. By being free to develop in the language you want and still communicate with Flash Media Server,all you need to do is create a web service. Flash Media Server 2 has some limitations on how web services handle complex data (objects) or an array returned by a web service. After some serious head scratching,I have attempted to draw out some of these limitations and describe to what extent you can still manipulate complex data types. By way of example—a web service providing a weather forecast using the Web Service Description Language (WSDL)—this article attempts to explain not only how web services work in Flash Media Server 2 but detail some of the challenges you face when dealing with complex data types. Exploring the simple forecast interfaceIn this section,I will go over the interface and the parameters required for the remote method. There are many free web services on the Internet (XMethods,?WebserviceX.net,and so on). I will use one of the WebserviceX.net services for this article (see Figure 1).
Figure 1. Weather Forecast application relying on Flash Media Server web services to display the information
The end user simply submits a five-digit ZIP code to the service. There's no real error handling built in. When results are displayed,the Next and Prev buttons step the user through the upcoming days in the forecast. Because there is a limit to the amount of data that Flash Media Server can receive from a web service,you may get mixed results with this weather forcast application. This web service provides a seven-day forecast but you will notice that,in most cases,you get a five-day forecast due to this limitation. The other result you may receive is an "Unable to connect to endpoint" message. Note:?If you do receive an "Unable to connect to endpoint" message,enter a new ZIP code and try again. There is no need to recompile the client application or unload the server ASC file. Alternate between ZIP codes 33021,77077,33004,33309,77079,94105,10286,and 97070. Of course,you can use your own ZIP code—just remember that this is a free service and is not always available. Point your browser to the following URL,which shows the XML structure definition for the Weather Forecast WSDL (see Listing 1). You should be able to see other methods that you could use,like? For this exercise,I will focus on? Listing 1.?Weather Forecast WSDL <wsdl:definitions targetNamespace="http://www.webservicex.net"> <wsdl:types> <s:schema elementFormDefault="qualified" targetNamespace="http://www.webservicex.net"> <s:element name="GetWeatherByZipCode"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ZipCode" type="s:string"/> </s:sequence> </s:complexType> </s:element> <s:element name="GetWeatherByZipCodeResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="GetWeatherByZipCodeResult" type="tns:WeatherForecasts"/> </s:sequence> </s:complexType> </s:element> <s:complexType name="WeatherForecasts"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Latitude" type="s:float"/> <s:element minOccurs="1" maxOccurs="1" name="Longitude" type="s:float"/> <s:element minOccurs="1" maxOccurs="1" name="AllocationFactor" type="s:float"/> <s:element minOccurs="0" maxOccurs="1" name="FipsCode" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="PlaceName" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="StateCode" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="Status" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="Details" type="tns:ArrayOfWeatherData"/> </s:sequence> </s:complexType> <s:complexType name="ArrayOfWeatherData"> <s:sequence> <s:element minOccurs="0" maxOccurs="unbounded" name="WeatherData" type="tns:WeatherData"/> </s:sequence> </s:complexType> <s:complexType name="WeatherData"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="Day" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="WeatherImage" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="MaxTemperatureF" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="MinTemperatureF" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="MaxTemperatureC" type="s:string"/> <s:element minOccurs="0" maxOccurs="1" name="MinTemperatureC" type="s:string"/> </s:sequence> </s:complexType> <s:element name="GetWeatherByPlaceName"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="PlaceName" type="s:string"/> </s:sequence> </s:complexType> </s:element> <s:element name="GetWeatherByPlaceNameResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="GetWeatherByPlaceNameResult" type="tns:WeatherForecasts"/> </s:sequence> </s:complexType> </s:element> <s:element name="WeatherForecasts" type="tns:WeatherForecasts"/> </s:schema> </wsdl:types> <wsdl:message name="GetWeatherByZipCodeSoapIn"> <wsdl:part name="parameters" element="tns:GetWeatherByZipCode"/> </wsdl:message> <wsdl:message name="GetWeatherByZipCodeSoapOut"> <wsdl:part name="parameters" element="tns:GetWeatherByZipCodeResponse"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameSoapIn"> <wsdl:part name="parameters" element="tns:GetWeatherByPlaceName"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameSoapOut"> <wsdl:part name="parameters" element="tns:GetWeatherByPlaceNameResponse"/> </wsdl:message> <wsdl:message name="GetWeatherByZipCodeHttpGetIn"> <wsdl:part name="ZipCode" type="s:string"/> </wsdl:message> <wsdl:message name="GetWeatherByZipCodeHttpGetOut"> <wsdl:part name="Body" element="tns:WeatherForecasts"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameHttpGetIn"> <wsdl:part name="PlaceName" type="s:string"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameHttpGetOut"> <wsdl:part name="Body" element="tns:WeatherForecasts"/> </wsdl:message> <wsdl:message name="GetWeatherByZipCodeHttpPostIn"> <wsdl:part name="ZipCode" type="s:string"/> </wsdl:message> <wsdl:message name="GetWeatherByZipCodeHttpPostOut"> <wsdl:part name="Body" element="tns:WeatherForecasts"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameHttpPostIn"> <wsdl:part name="PlaceName" type="s:string"/> </wsdl:message> <wsdl:message name="GetWeatherByPlaceNameHttpPostOut"> <wsdl:part name="Body" element="tns:WeatherForecasts"/> </wsdl:message> <wsdl:portType name="WeatherForecastSoap"> <wsdl:operation name="GetWeatherByZipCode"> <documentation> Get one week weather forecast for a valid Zip Code(USA) </documentation> <wsdl:input message="tns:GetWeatherByZipCodeSoapIn"/> <wsdl:output message="tns:GetWeatherByZipCodeSoapOut"/> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <documentation> Get one week weather forecast for a place name(USA) </documentation> <wsdl:input message="tns:GetWeatherByPlaceNameSoapIn"/> <wsdl:output message="tns:GetWeatherByPlaceNameSoapOut"/> </wsdl:operation> </wsdl:portType> <wsdl:portType name="WeatherForecastHttpGet"> <wsdl:operation name="GetWeatherByZipCode"> <documentation> Get one week weather forecast for a valid Zip Code(USA) </documentation> <wsdl:input message="tns:GetWeatherByZipCodeHttpGetIn"/> <wsdl:output message="tns:GetWeatherByZipCodeHttpGetOut"/> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <documentation> Get one week weather forecast for a place name(USA) </documentation> <wsdl:input message="tns:GetWeatherByPlaceNameHttpGetIn"/> <wsdl:output message="tns:GetWeatherByPlaceNameHttpGetOut"/> </wsdl:operation> </wsdl:portType> <wsdl:portType name="WeatherForecastHttpPost"> <wsdl:operation name="GetWeatherByZipCode"> <documentation> Get one week weather forecast for a valid Zip Code(USA) </documentation> <wsdl:input message="tns:GetWeatherByZipCodeHttpPostIn"/> <wsdl:output message="tns:GetWeatherByZipCodeHttpPostOut"/> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <documentation> Get one week weather forecast for a place name(USA) </documentation> <wsdl:input message="tns:GetWeatherByPlaceNameHttpPostIn"/> <wsdl:output message="tns:GetWeatherByPlaceNameHttpPostOut"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="WeatherForecastSoap" type="tns:WeatherForecastSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <wsdl:operation name="GetWeatherByZipCode"> <soap:operation soapAction="http://www.webservicex.net/GetWeatherByZipCode" style="document"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <soap:operation soapAction="http://www.webservicex.net/GetWeatherByPlaceName" style="document"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:binding name="WeatherForecastHttpGet" type="tns:WeatherForecastHttpGet"> <http:binding verb="GET"/> <wsdl:operation name="GetWeatherByZipCode"> <http:operation location="/GetWeatherByZipCode"/> <wsdl:input> <http:urlEncoded/> </wsdl:input> <wsdl:output> <mime:mimeXml part="Body"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <http:operation location="/GetWeatherByPlaceName"/> <wsdl:input> <http:urlEncoded/> </wsdl:input> <wsdl:output> <mime:mimeXml part="Body"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:binding name="WeatherForecastHttpPost" type="tns:WeatherForecastHttpPost"> <http:binding verb="POST"/> <wsdl:operation name="GetWeatherByZipCode"> <http:operation location="/GetWeatherByZipCode"/> <wsdl:input> <mime:content type="application/x-www-form-urlencoded"/> </wsdl:input> <wsdl:output> <mime:mimeXml part="Body"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="GetWeatherByPlaceName"> <http:operation location="/GetWeatherByPlaceName"/> <wsdl:input> <mime:content type="application/x-www-form-urlencoded"/> </wsdl:input> <wsdl:output> <mime:mimeXml part="Body"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="WeatherForecast"> <documentation> Get one week weather forecast for valid zip code or Place name in USA </documentation> <wsdl:port name="WeatherForecastSoap" binding="tns:WeatherForecastSoap"> <soap:address location="http://www.webservicex.net/WeatherForecast.asmx"/> </wsdl:port> <wsdl:port name="WeatherForecastHttpGet" binding="tns:WeatherForecastHttpGet"> <http:address location="http://www.webservicex.net/WeatherForecast.asmx"/> </wsdl:port> <wsdl:port name="WeatherForecastHttpPost" binding="tns:WeatherForecastHttpPost"> <http:address location="http://www.webservicex.net/WeatherForecast.asmx"/> </wsdl:port> </wsdl:service></wsdl:definitions>
Examining the server-side code On the server side,make sure you start by loading the? load("webservices/WebServices.asc");
In this particular example,you are using remote method invocation (RMI) from the client to the server method Client.prototype.getWeather = function (zip){
Then you need a var of type? var aDetail=[];var Detailing="";var WSDLuri = "http://www.webservicex.net/WeatherForecast.asmx?WSDL";
Instantiate the? WeatherService = new WebService(WSDLuri);
Keep in mind that the? So you could use the following: WeatherService.onLoad = function(Wsdl){ trace(Wsdl); } // onFault is triggered if the above load fails WeatherService.onFault = function(fault){ trace( fault.faultstring ); clientNow.call("failed",null,fault.faultstring); }
Once your? As shown in Listing 1,pointing the browser to the URL reveals which methods are available to call within the XML code definition presented by the browser and how they will return the results. Currently,you are concerned with the? forcast =WeatherService.GetWeatherByZipCode(zip);
Once your method is called,you need to parse the result when? forcast.onResult = function(returning) { //convert result into an array aDetail for (var i in returning) { if (i == "Details") { for (var j in returning[i]) { switch (j) { case "xmlNodes" : //get rid of commas for (var p in returning[i][j]) { if (returning[i][j][p] != undefined) { aDetail.push(returning[i][j][p].toString()); } } case "length" : var total = returning[i][j]; } } } } var tot= aDetail.length; //push each array slot into the string Detailing to form xml string for (var i = 1; i<tot; ++i) { Detailing += aDetail[i]; } aDetail.splice(0); //now send xml string to client clientNow.call("forcast",Detailing); }
This process is rather elementary. All you're doing is working around the current? for (var p in returning[i][j]) { if (returning[i][j][p] != undefined) { aDetail.push(returning[i][j][p].toString()); }}
Once you have passed in all nodes,you can manipulate the array directly. Keep in mind that every web service returns results in its own way,so it is important to find a way to unfold your result. I have found that the most reliable way is to use the "for in" loop. Applying a "for in" loop showed some promise to the wealth of data returned. I then found other objects and set up "for in" loops in them,as well,until I had all the data I needed. Note:?If you handle the processing on the client side,the "for in" loops will read backwards. Therefore,it is important to reverse the array (using? At this point you could use just the array to do what you want but my main objective is to get the data into its proposed XML format: for (var i = 1; i<tot; ++i) { Detailing += aDetail[i];}
Push each node into the? clientNow.call("forcast",Detailing);
Examining the client-side code As you may be able to determine in Figure 1,I used three buttons ("Prev," "Next," and "Get Forecast"),a text input field (
In the Main.as class I have the following: m_nc.forcast = function(returning):Void { this.owner.ReturnForcast(returning); };
which passes results to its own? private function ReturnForcast(Detailing):Void { B.enabled = true; var my2_xml:XML = new XML(Detailing); var parentNode = my2_xml.childNodes; var xTotal:Number = parentNode.length; for (var i = 0; i<xTotal; ++i) { var subs:Number = parentNode[i].childNodes.length; var day = parentNode[i].childNodes[0].firstChild.nodeValue; var wImg = parentNode[i].childNodes[1].firstChild.nodeValue; var hF = parentNode[i].childNodes[2].firstChild.nodeValue; var lF = parentNode[i].childNodes[3].firstChild.nodeValue; var hC = parentNode[i].childNodes[4].firstChild.nodeValue; var lC = parentNode[i].childNodes[5].firstChild.nodeValue; aWeather.push([day,wImg,hF,lF,hC,lC]); }T_text.html = true;showWeather(); }
Once? private function showWeather() { if (aWeather[current][0] != undefined) { T_text.text = ""; T_text.text += aWeather[current][0]+"<br>"; T_text.text += "<img src='"+aWeather[current][1]+"'><br>"; T_text.text += "high F="+aWeather[current][2]+"<br>"; T_text.text += "low F="+aWeather[current][3]+"<br>"; T_text.text += "high C="+aWeather[current][4]+"<br>"; T_text.text += "low C="+aWeather[current][5]+"<br>"; } prev.enabled =(current>0? true:false); dnxt.enabled =(current<aWeather.length?true:false); }
To make the actual remote method call from the client,use the? private function sendBtn() { var scope = this._parent; scope.current = 0; if (scope.z_text.text.length == 5) { scope.prev.enabled = false; scope.dnxt.enabled = false; scope.B.enabled = false; scope.m_nc.call("getWeather",scope.z_text.text); scope.T_text.text = "please waitn LOADING...."; } else { scope.T_text.text = "enter your 5digit zip code above"; }}
Reviewing the final codeYour final client-side code should look like this: import mx.controls.*;//manipulation on Server sideclass Main extends MovieClip { private var current:Number = 0; private var Kconn:String = "rtmp:/webSD"; private var aWeather:Array = new Array(); private var T_text:TextArea; private var z_text:TextInput; private var B:Button; private var prev:Button; private var dnxt:Button; private var m_nc:NetConnection; function Main(Void) { this.attachMovie("TextInput","z_text",2,{_y:15}); z_text.setSize(74,22); this.attachMovie("TextArea","T_text",4,{_y:z_text._y+z_text.height+3}); T_text.setSize(178,141); T_text.html=false; T_text.wordWrap=true; trace("constructor inititalized!"); } //Called by the main timeline function connectMe(Void):Void { m_nc = new NetConnection(); m_nc.owner = this; m_nc.onStatus = function(info:Object):Void { this.owner.onConnectionStatus(info); }; m_nc.forcast = function(returning):Void { trace("try returning from server"+returning); this.owner.ReturnForcast(returning); }; m_nc.failed = function(failedString:String):Void { this.owner.Reset(failedString); }; m_nc.connect(Kconn); } private function onConnectionStatus(info:Object):Void { trace("nc.onStatus> info.code: "+info.code); if (info.code == "NetConnection.Call.Failed") { Reset(info.code); } if (info.description) { trace("nc.onStatus> info.description: "+info.description); } } //recieve forcast from the Server's webservice private function ReturnForcast(Detailing):Void { B.enabled = true; var my2_xml:XML = new XML(Detailing); var parentNode = my2_xml.childNodes; var xTotal:Number = parentNode.length; for (var i = 0; i<xTotal; ++i) { var subs:Number = parentNode[i].childNodes.length; var day = parentNode[i].childNodes[0].firstChild.nodeValue; var wImg = parentNode[i].childNodes[1].firstChild.nodeValue; var hF = parentNode[i].childNodes[2].firstChild.nodeValue; var lF = parentNode[i].childNodes[3].firstChild.nodeValue; var hC = parentNode[i].childNodes[4].firstChild.nodeValue; var lC = parentNode[i].childNodes[5].firstChild.nodeValue; aWeather.push([day,lC]); } //aWeather.reverse(); //trace("003 "+aWeather); T_text.html = true; showWeather(); } //produce visual representation of data stored in array private function showWeather() { //trace("004--> showWeather()"+current); if (aWeather[current][0] != undefined) { //::TODO proper error handling //(current == aWeather.length ? --current : ++current); T_text.text = ""; T_text.text += aWeather[current][0]+"<br>"; T_text.text += "<img src='"+aWeather[current][1]+"'><br>"; T_text.text += "high F="+aWeather[current][2]+"<br>"; T_text.text += "low F="+aWeather[current][3]+"<br>"; T_text.text += "high C="+aWeather[current][4]+"<br>"; T_text.text += "low C="+aWeather[current][5]+"<br>"; } prev.enabled =(current>0? true:false); dnxt.enabled =(current<aWeather.length?true:false); } private function Reset(failed:String) { trace("reset called"); T_text.text = failed; B.enabled = true; } //load public function onLoad(){ B.addEventListener("click",this.sendBtn); prev.addEventListener("click",this.prevBtn); dnxt.addEventListener("click",this.nextBtn); B.label = "Get Forecast"; prev.label = "prev"; dnxt.label = "next"; } //next button call private function nextBtn() { var scope = this._parent; ++scope.current; scope.showWeather(); } //prev button call private function prevBtn() { var scope = this._parent; --scope.current; scope.showWeather(); } //send(B) zip button call private function sendBtn() { var scope = this._parent; scope.current = 0; if (scope.z_text.text.length == 5) { scope.prev.enabled = false; scope.dnxt.enabled = false; scope.B.enabled = false; scope.m_nc.call("getWeather",scope.z_text.text); scope.T_text.text = "please waitn LOADING...."; } else { scope.T_text.text = "enter your 5digit zip code above"; } }}
Your final server-side code should look like this: //most methods directly reflect the client side methods of webservicesload("webservices/WebServices.asc");application.onAppStart = function(){ }application.onConnect = function(N_client){ trace("user has connected"); application.acceptConnection(N_client); }Client.prototype.getWeather = function (zip){ var aDetail=[]; var Detailing=""; var clientNow=this; var WSDLuri = "http://www.webservicex.net/WeatherForecast.asmx?WSDL"; // Service Created WeatherService = new WebService(WSDLuri); // load WSDL WeatherService.onLoad = function(Wsdl){ trace("wsdl-->string"+Wsdl); } // onFault is triggered if the above load fails WeatherService.onFault = function(fault){ trace("wetherservice fault-->"+ fault.faultstring ); clientNow.call("failed",fault.faultstring); } //forcast is set to recieve result from the remote method "GetWeatherByZipCode" forcast =WeatherService.GetWeatherByZipCode(zip); // Grab the result forcast.onResult = function(returning) { //convert result into an array aDetail for (var i in returning) { if (i == "Details") { for (var j in returning[i]) { switch (j) { case "xmlNodes" : //get rid of commas for (var p in returning[i][j]) { if (returning[i][j][p] != undefined) { aDetail.push(returning[i][j][p].toString()); } } case "length" : var total = returning[i][j]; } } } } var tot= aDetail.length; //push each array slot into the string "Detailing" to form xml string for (var i = 1; i<tot; ++i) { Detailing += aDetail[i]; } aDetail.splice(0); //now send xml string to client clientNow.call("forcast",Detailing); } // if Above fails provide error forcast.onFault = function(fault){ trace( "fault->"+fault.faultstring ); clientNow.call("failed",fault.faultstring); }}
Where to go from hereThis simple example should provide you with a better idea of the web services limitations that you will encounter. Having taken the time to explore the possibilities,however,you can see that when you use Flash Media Server 2,you have the freedom to interface with not only any language but with any result set as well. For more information,check out the following resources for various technologies:
You may also wish to read up on?XML-RPC,a "spec and set of implementations that allow software running on disparate operating systems,running in different environments to make procedure calls over the Internet." (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- scala – 如何在不转换DataFrame并访问数据集的情况下向数据
- 全面支持 Angular2 的 Web 后台 Bootstrap 模板 Sing App –
- twitter-bootstrap – Twitter Bootstrap 3:右对齐的折叠导
- 使用Ansible docker_service模块将服务部署到swarm
- scala – 数据流的SHA256
- bash – 输出特定行大文本文件
- AngularJS:指令中的缩小问题
- twitter-bootstrap – Adsense响应:availableWidth = 0没有
- angularjs – UI-Router隐藏父模板
- typescript – 根据Angular2中的枚举选择