Working with Request/Response Data Communication

You can work with request/response data communication in three basic ways: via simple HTTP services (including REST services and services using JSON), SOAP web services, and Remoting. Each achieves the same basic goal of sending a request and receiving a response, and as such you can use them for the same purposes within Flex applications. Which method you choose depends primarily on what type of service you have available. For example, if you want to load XML data from an XML document you should use simple HTTP service communication. However, if you want to call a web service method, you should use web services communication.

Simple HTTP Services

The most basic type of HTTP request/response communication uses what we call simple HTTP services. These services include things such as text and XML resources, either in static documents or dynamically generated by something such as a ColdFusion page, a servlet, or an ASP.NET page. Simple HTTP services might also include pages that run scripts when called in order to do things such as insert data into or update databases or send email. You can use simple HTTP services to execute these sorts of server behaviors, to load data, or to do both.

Flex provides two basic ways in which you can call simple HTTP services: using HTTPService, a Flex framework component; and using the Flash Player class flash.net.URLLoader.

HTTPService

HTTPService is a component that allows you to make requests to simple HTTP services such as text files, XML files, or scripts and pages that return dynamic (or static) data. You must always define a value for the url property of an HTTPService object. The url property tells the object where it can find the resource to which it should make the request. The value can be either a relative URL or an absolute URL. The following example uses MXML to create an HTTPService object that loads text from a file called data.txt saved in the same directory as the compiled .swf file:

<mx:HTTPService id="textService" url="data.txt" />

Now that you know how to create a new HTTPService instance, let’s discuss how to send requests, handle results, and pass parameters.

Sending requests

Creating an HTTPService object does not automatically make the request to load the data. To make the request, you must call the send() method. You can call the send() method in response to any framework or user event. For example, if you want to make the request as soon as the application initializes, you can call send() in response to the initialize event. If you want to load the data when the user clicks a button, you can call the send() method in response to a click event:

textService.send();

Handling responses

The send() method makes the request, but a response is not likely to be returned instantaneously. Instead, the application must wait for a result event. The result event occurs when the entire response has been returned. The following example displays an alert when the data loads:

<mx:HTTPService id="textService" url="data.txt"
    result="Alert.show('Data loaded')" />

Of course, normally you would want to do something more useful than display an alert when the data loads. More commonly, you will want to use the data in some way. You can retrieve the response data (i.e., the data that has loaded) using the lastResult property. Plain text is always loaded as string data. However, the HTTPService component is capable of automatically converting serialized data into associative arrays. For this reason, the lastResult property is typed as Object. If you want to treat it as a string, you must cast it. Example 17-1 loads text from a file and then displays it in a text area.

Example 17-1. Loading text with HTTPService

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="
com.oreilly.programmingflex.rpc.*" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private function initializeHandler(event:Event):void {
                textService.send();
            }

            private function resultHandler(event:Event):void {
                textArea.text = String(textService.lastResult);
            }

        ]]>
    </mx:Script>

    <mx:HTTPService id="textService" url="data.txt" result="resultHandler(event)" />

    <mx:TextArea id="textArea" />

</mx:Application>

Although you can explicitly handle the result event, it is far more common to use data binding. Example 17-2 accomplishes the same thing as Example 17-1, but it uses data binding.

Example 17-2. Using data binding with HTTPService

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="
com.oreilly.programmingflex.rpc.*" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private function initializeHandler(event:Event):void {
                textService.send();
            }

        ]]>
    </mx:Script>

    <mx:HTTPService id="textService" url="data.txt" />

    <mx:TextArea id="textArea" text="{textService.lastResult}" />

</mx:Application>

When possible, HTTPService will deserialize data it loads in much the same way as it would interpret data placed in a Model tag. For example, consider the following data:

<countries>
  <country>Select One</country>
  <country>Canada</country>
  <country>U.S.</country>
</countries>

If you attempt to load this data using HTTPService, it will be parsed into an object named countries that contains an array named country, each element of which corresponds to the <country> elements. Example 17-3 illustrates this using a live XML file that contains the XML data shown in the preceding code block. It uses data binding to populate the combo box with the data.

Example 17-3. Loading XML with HTTPService

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private function initializeHandler(event:Event):void {
                countriesService.send();
            }

        ]]>
    </mx:Script>

    <mx:HTTPService id="countriesService"
url="http://www.rightactionscript.com/states/xml/countries.xml" />

    <mx:VBox>
        <mx:ComboBox id="country"
            dataProvider="{countriesService.lastResult.countries.country}"  />
    </mx:VBox>

</mx:Application>

As we’ve already seen, by default HTTPService results are interpreted as text if they are blocks of text, and if the results are XML data, they're parsed into an object. However, that's merely the default behavior. You can explicitly dictate the way in which the results are handled using the resultFormat property of the HTTPService object. The default value is object, which yields the default behavior you’ve already seen. You can optionally specify any of the following values:

text

The data is not parsed at all, but is treated as raw text.

flashvars

The data is assumed to be in URL-encoded format, and it will be parsed into an object with properties corresponding to the name/value pairs.

array

The data is assumed to be in XML format, and it is parsed into objects much the same as with the object settings. However, in this case, the result is always an array. If the returned data does not automatically parse into an array, the parsed data is placed into an array.

xml

The data is assumed to be in XML format, and it is interpreted as XML using the legacy XMLNode ActionScript class.

e4x

The data is assumed to be in XML format, and it is interpreted as XML using the ActionScript 3.0 XML class (E4X).

Sending parameters

When you want to pass parameters to the service, you can use the request property of the HTTPService instance. The request property requires an Object value. By default, the name/value pairs of the object are converted to URL-encoded format and are sent to the service using HTTP GET. You can assign an object using ActionScript, as in the following:

var parameters:Object = new Object();
parameters.a = "one";
parameters.b = "two";
service.request = parameters;

However, when creating an HTTPService object using MXML, it's often convenient to declare the parameters using MXML as well:

<mx:HTTPService id="service" url="script.php">
  <mx:request>
    <a>one</a>
    <b>two</b>
  </mx:request>
</mx:HTTPService>

Declaring the request in this way also allows you to use data binding with the parameters. To illustrate this with a working example, consider the code in Example 17-4, which builds on Example 17-3 by using a second HTTPService object to retrieve state names based on the selected country.

Example 17-4. Using HTTPService with input parameters

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private function initializeHandler(event:Event):void {
                countriesService.send();
            }

            private function changeHandler(event:Event):void {
                statesService.send();
            }

        ]]>
    </mx:Script>

    <mx:HTTPService id="countriesService"
url="http://www.rightactionscript.com/states/xml/countries.xml" />

    <mx:HTTPService id="statesService"
url="http://www.rightactionscript.com/states/xml/states.php">
        <mx:request>
            <country>
                {country.value}
            </country>
        </mx:request>
    </mx:HTTPService>

    <mx:VBox>
        <mx:ComboBox id="country"
            dataProvider="{countriesService.lastResult.countries.country}"
            change="changeHandler(event)" />
        <mx:ComboBox dataProvider="{statesService.lastResult.states.state}" />
    </mx:VBox>

</mx:Application>

In the preceding example, the first combo box is populated with a list of countries. When the user selects a country from the combo box, it sends a request to the second service, a PHP script, sending the selected country as a parameter. The return value is in the following format:

<states>
  <state>Alabama</state>
  <state>Alaska</state>
  <!-- etc. -->
</states>

As noted, by default, parameters are sent in URL-encoded format using HTTP GET. However, you can adjust those settings. The contentType property of the HTTPService object determines the format in which the content is sent. The default value is application/x-www-form-urlencoded, which sends the values in URL-encoded format. You can specify application/xml to send the data as XML if the service expects raw XML data:

<mx:HTTPService id="service" url="script.php" contentType="application/xml">
  <mx:request>
    <parameters>
      <a>one</a>
      <b>two</b>
    </parameters>
  </mx:request>
</mx:HTTPService>

The method property determines what transport method is used. The default is GET, but you can also specify a value of POST, HEAD, OPTIONS, PUT, TRACE, or DELETE, though Flash Player supports only GET and POST when running in a browser (AIR applications support all methods).

Using HTTPService with ActionScript

Although the simplest and quickest way to use an HTTPService object is to primarily use MXML, this technique is best-suited to nonenterprise applications in which the data communication scenarios are quite simple. However, for more complex data communication requirements, it is advisable to use remote proxies, as discussed earlier in this chapter. Because HTTPService components provide significant data conversion advantages (such as automatic serialization of data), it is still frequently a good idea to use an HTTPService object within a remote proxy. However, it is generally necessary to then work with the HTTPService component entirely with ActionScript, including constructing the object and handling the responses.

When working with HTTPService objects entirely with ActionScript, you’ll want to import the mx.rpc.http.HTTPService class. You can then construct an instance with a standard new statement:

var httpRequest:HTTPRequest = new HTTPRequest();

You should then set the url property:

httpRequest.url = "data.txt";

Just as you would listen for any event from any object, you need to add listeners to HTTPService objects using addEventListener(). HTTPService objects dispatch events of type ResultEvent when a response is returned, and they dispatch events of type FaultEvent when an error is returned from the server. The ResultEvent and FaultEvent classes are in the mx.rpc.events package:

httpRequest.addEventListener(ResultEvent.RESULT, resultHandler);

Example 17-5 is a simple working example that uses the recommended remote proxy approach in conjunction with HTTPService. This example accomplishes the same basic thing as previous MXML-based examples—displaying countries and states in combo boxes. However, this example uses several classes to accomplish this. The first class we’ll look at is a simple data model class called ApplicationDataModel. Here’s the code.

Example 17-5. ApplicationDataModel.as

package com.oreilly.programmingflex.remotedata {
    import mx.collections.ListCollectionView;

    public class ApplicationDataModel {

        private static var _instance:ApplicationDataModel;

        private var _countryNames:ListCollectionView;
        private var _statesNames:ListCollectionView;

        [Bindable]
        public function set countryNames(value:ListCollectionView):void {
            _countryNames = value;
        }

        public function get countryNames():ListCollectionView {
            return _countryNames;
        }

        [Bindable]
        public function set statesNames(value:ListCollectionView):void {
            _statesNames = value;
        }

        public function get statesNames():ListCollectionView {
            return _statesNames;
        }

        public function ApplicationDataModel() {}

        public static function getInstance():ApplicationDataModel {
            if(_instance == null) {
                _instance = new ApplicationDataModel();
            }
            return _instance;
        }
    }
}

In Example 17-6, we’ll define StatesService, the remote proxy that loads XML data using HTTPService. The class defines two service methods: getCountries() and getStates(). When the results are returned for the service method calls, they're assigned to the data model.

Example 17-6. StatesService.as

package com.oreilly.programmingflex.remotedata {
    import mx.rpc.http.HTTPService;
    import mx.rpc.events.ResultEvent;
    import mx.collections.XMLListCollection;
    import com.oreilly.programmingflex.remotedata.ApplicationDataModel;

    public class StatesService {

        private var _service:HTTPService;

        public function StatesService() {
            _service = new HTTPService();
            _service.resultFormat = "e4x";
        }

        public function getCountries():void {
            _service.addEventListener(ResultEvent.RESULT, countriesResultHandler);
            _service.url = "http://rightactionscript.com/states/xml/countries.xml";
            _service.send();
        }

        public function getStates(country:String):void {
            _service.addEventListener(ResultEvent.RESULT, statesResultHandler);
            _service.url = "http://rightactionscript.com/states/xml/
states.php?country=" + country;
            _service.send();
        }

        private function countriesResultHandler(event:ResultEvent):void {
            _service.removeEventListener(ResultEvent.RESULT, countriesResultHandler);
            ApplicationDataModel.getInstance().countryNames = new
XMLListCollection(_service.lastResult.children() as XMLList);
        }

        private function statesResultHandler(event:ResultEvent):void {
            _service.removeEventListener(ResultEvent.RESULT, statesResultHandler);
            ApplicationDataModel.getInstance().statesNames = new
XMLListCollection(_service.lastResult.children() as XMLList);
        }
    }
}

Example 17-7 is the MXML application that utilizes both of these classes.

Example 17-7. Using the states service proxy and application data model

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
creationComplete="creationCompleteHandler(event)">
    <mx:Script>
        <![CDATA[
            import mx.rpc.http.HTTPService;
            import mx.rpc.events.ResultEvent;
            import com.oreilly.programmingflex.remotedata.StatesService;
            import com.oreilly.programmingflex.remotedata.ApplicationDataModel;

            private var _statesService:StatesService;
            private function creationCompleteHandler(event:Event):void {
                _statesService = new StatesService();
                _statesService.getCountries();
            }
        ]]>
    </mx:Script>
    <mx:VBox>
        <mx:ComboBox id="countryMenu"
dataProvider="{ApplicationDataModel.getInstance().countryNames}"
change="_statesService.getStates(countryMenu.selectedLabel)" />
        <mx:ComboBox
         dataProvider="{ApplicationDataModel.getInstance().statesNames}" />
    </mx:VBox>
</mx:Application>

In this example, the StatesService instance is created, and getCountries() is called immediately. The first combo box is data-bound to the countryNames property of the data model. As the user selects a value from the first combo box, it calls getStates(), passing it the selected country. The second combo box is data-bound to the statesNames property of the data model.

URLLoader

HTTPService allows you to use requests and handle responses to and from simple HTTP services. You can optionally use the Flash Player class called flash.net.URLLoader to accomplish the same tasks entirely with ActionScript, but at a slightly lower level. Practically speaking, there is little to no difference between using URLLoader and HTTPService. However, because many developers building pure ActionScript 3 libraries will rely on URLLoader (and you may rely on their code), you will likely find it useful to be familiar with how to use URLLoader.

The first step when working with a URLLoader object is always to construct the object using the constructor method, as follows:

var loader:URLLoader = new URLLoader();

Once you’ve constructed the object, you can do the following:

  • Send requests.

  • Handle responses.

  • Send parameters.

Sending requests

You can send requests using the load() method of a URLLoader object. The load() method requires that you pass it a flash.net.URLRequest object specifying at a minimum what URL to use when making the request. The following makes a request to a text file called data.txt:

loader.load(new URLRequest("data.txt"));

Handling responses

URLLoader objects dispatch complete events when a response has been returned. Any return value is stored in the data property of the URLLoader object. Example 17-8 loads XML data from a URL and handles the response.

Example 17-8. Loading XML using URLLoader

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private var _countriesService:URLLoader;

            private function initializeHandler(event:Event):void {
                _countriesService = new URLLoader();
                _countriesService.addEventListener(Event.COMPLETE,
countriesCompleteHandler);
                _countriesService.load(new
URLRequest("http://www.rightactionscript.com/states/xml/countries.xml"));
                XML.ignoreWhitespace = true;
            }

            private function countriesCompleteHandler(event:Event):void {
                var xml:XML = new XML(_countriesService.data);
                country.dataProvider = xml.children();
            }

        ]]>
    </mx:Script>

    <mx:VBox>
        <mx:ComboBox id="country" />
    </mx:VBox>

</mx:Application>

When data is returned to a URLLoader object, it's interpreted as a string by default. In the preceding example, you can see that this is so because the data must be converted to an XML object.

It is possible to receive binary data and URL-encoded data in response to a URLLoader request. If you want to handle a binary response, you must set the dataFormat property of the URLLoader object to flash.net.URLLoaderDataFormat.BINARY. Binary data will then be interpreted as a ByteArray. If the returned data is in URL-encoded format, you can set the dataFormat property to flash.net.URLLoaderDataFormat.VARIABLES and the returned data will be interpreted as a flash.net.URLVariables object. URLVariables objects contain properties corresponding to the name/value pairs in the returned value. For example, if a URLLoader object is set to handle URL-encoded return data, a return value of a=one&b=two will create a URLVariables object with a and b properties accessible, as in the following:

trace(loader.data.a + " " + loader.data.b);

Sending parameters

You can send parameters using URLLoader as well. To send parameters, you assign a value to the data property of the URLRequest object used to make the request. The URLRequest object can send binary data or string data. If you assign a ByteArray to the data property it's sent as binary. If you assign a URLVariables object to the data property, the data is sent in URL-encoded format. Otherwise, the data is converted to a string. Example 17-9 builds on Example 17-8 to send a parameter when requesting state data.

Example 17-9. Sending parameters with URLLoader

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[

            private var _countriesService:URLLoader;
            private var _statesService:URLLoader;

            private function initializeHandler(event:Event):void {
                _countriesService = new URLLoader();
                _countriesService.addEventListener(Event.COMPLETE,
countriesCompleteHandler);
                _countriesService.load(new
URLRequest("http://www.rightactionscript.com/states/xml/countries.xml"));
                _statesService = new URLLoader();
                _statesService.addEventListener(Event.COMPLETE,
statesCompleteHandler);
                XML.ignoreWhitespace = true;
            }

            private function countriesCompleteHandler(event:Event):void {
                var xml:XML = new XML(_countriesService.data);
                country.dataProvider = xml.children();
            }

            private function statesCompleteHandler(event:Event):void {
                var xml:XML = new XML(_statesService.data);
                state.dataProvider = xml.children();
            }

            private function changeHandler(event:Event):void {
                var request:URLRequest = new
URLRequest("http://www.rightactionscript.com/states/xml/states.php");
                var parameters:URLVariables = new URLVariables();
                parameters.country = country.value;
                request.data = parameters;
                _statesService.load(request);
            }

        ]]>
    </mx:Script>

    <mx:VBox>
        <mx:ComboBox id="country" change="changeHandler(event)" />
        <mx:ComboBox id="state" />
    </mx:VBox>

</mx:Application>

You can use the method property to specify how the data should be sent. Possible values are flash.net.URLRequestMethod.POST and flash.net.URLRequestMethod.GET.

Using URLLoader in a remote proxy

Now that we’ve had a chance to see the basics of working with URLLoader, here’s an example that uses URLLoader in context. In Using HTTPService with ActionScript" you saw a complete working example. You can use the same MXML document and data model class and make a few minor edits to the remote proxy class to use URLLoader instead of HTTPService. Example 17-10 is the new remote proxy class.

Example 17-10. The new StatesService.as

package com.oreilly.programmingflex.remotedata {
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.events.Event;
    import flash.net.URLVariables;
    import mx.collections.XMLListCollection;
    import com.oreilly.programmingflex.remotedata.ApplicationDataModel

    public class StatesService {

        private var _service:URLLoader;

        public function StatesService() {
            _service = new URLLoader();
        }

        public function getCountries():void {
            _service.addEventListener(Event.COMPLETE, countriesResultHandler);
            var request:URLRequest = new
URLRequest("http://rightactionscript.com/states/xml/countries.xml");
            _service.load(request);
        }

        public function getStates(country:String):void {
            _service.addEventListener(Event.COMPLETE, statesResultHandler);
            var request:URLRequest = new
URLRequest("http://rightactionscript.com/states/xml/states.php");
            var parameters:URLVariables = new URLVariables();
            parameters.country = country;
            request.data = parameters;
            _service.load(request);
        }

        private function countriesResultHandler(event:Event):void {
            _service.removeEventListener(Event.COMPLETE, countriesResultHandler);
            ApplicationDataModel.getInstance().countryNames =
new XMLListCollection(new XML(_service.data).children() as XMLList);
        }

        private function statesResultHandler(event:Event):void {
            _service.removeEventListener(Event.COMPLETE, statesResultHandler);
            ApplicationDataModel.getInstance().statesNames =
new XMLListCollection(new XML(_service.data).children() as XMLList);
        }


    }
}

You can test this new StatesService class using the same MXML document and ApplicationDataModel class from earlier in the chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset