Creating an HTTPService object The MXML syntax to create an HTTPService object looks like this: As with all Flex components, the object’s id property is used to identify it uniquel
Trang 18 Save and run the application As shown in Figure 23.9, you should see that the
applica-tion automatically retrieves and displays the data upon startup
FIGURE 23.9
Data returned using code generated by Flash Builder
Listing 23.2 shows the completed application code Notice that the use of the generated service and value object classes makes the application code itself very simple
getContactsResult.token = contactService.getContacts();
} ]]>
Trang 2<mx:DataGridColumn headerText=”First Name” dataField=”firstname”/>
<mx:DataGridColumn headerText=”Last Name” dataField=”lastname”/>
<mx:DataGridColumn headerText=”Email” dataField=”email”/>
<mx:DataGridColumn headerText=”Phone” dataField=”phone”/>
You can declare and configure an HTTPService object in either MXML or ActionScript code For applications that communicate only with a single network resource, the MXML approach might be simpler and easier to implement In more complex applications that use multiple network resources, ActionScript classes and methods that dynamically generate HTTPService objects as needed can be easier to manage
Creating an HTTPService object
The MXML syntax to create an HTTPService object looks like this:
<fx:Declarations>
<s:HTTPService id=”myService” url=”data/contacts.xml”/>
</fx:Declarations>
As with all Flex components, the object’s id property is used to identify it uniquely in the context
of the current application or component The url property (discussed in detail in the next section) can be set with either a literal String, as in this example, or with a binding expression if you want to be able to switch to a different network resource at runtime
Trang 3New Feature
As with all nonvisual components, an HTTPService object declared in MXML must be wrapped in an
<fx:Declarations> element in Flex 4 n
Alternatively, you can declare the object first and then set its url in a separate statement:
private var myService:HTTPService = new HTTPService();
myService.url = “data/contacts.xml”;
The Flex framework’s class library has two HTTPService classes The first, which is imported in the preceding example, is a member of the mx.rpc.http package and is used in ActionScript code The other version of the HTTPService class is a subclass of the first and is a member of the
mx.rpc.http.mxml package This is the version you use when you instantiate the object with the <s:HTTPService> tag
The versions are nearly identical with only one difference The MXML version implements an
initialized() method that’s designed to be called after the object’s instantiation When you declare the HTTPService object in MXML, this method is called automatically; you only need to call it yourself if you instantiate this version of the class in ActionScript code
New Feature
In Flex 3, the showBusyCursor property, which causes an animated cursor to be displayed for the duration
of an HTTPService request/response cycle, and the concurrency property, which determines how ple concurrent requests to the same network resource are handled, were declared in the MXML version of HTTPService In Flex 4, these have been moved to the superclass, mx.rpc.http.HTTPService, and are therefore available regardless of how you declare or instantiate the RPC object n
multi-Tip
ActionScript-based declarations of HTTPService should be placed outside of any function declarations, so that the object is accessible from all functions in the current application or component n
Essential HTTPService properties
Whether you use the MXML or ActionScript approach, the HTTPService component implements these properties that determine where the request is made and what HTTP methods are used:
Trang 4l concurrency:String A rule that determines how to handle multiple concurrent calls
to the same network resource
l method:String The HTTP method to be used
l resultFormat:String The format in which the data should be returned
l showBusyCursor:Boolean When true, causes an animated cursor to appear for the duration of the request/response cycle
l url:String The Web address to which the request is sent
The details of these properties are described in the following sections
Setting the url property
The url property is set to the network address to which the HTTP service should be sent For a Flex application designed for Web deployment, this can be either a relative or absolute address
For example, if the Flex application is retrieving a static XML file that’s on the same server and within the same directory structure, the url could be set as follows:
myHTTPService.url = “data/contacts.xml”;
For Web-based applications, the expression data/contacts.xml means that the XML file is in
a data subfolder on the same server from which the Flex application downloads at runtime
For desktop applications deployed with Adobe AIR, or for Web applications that need to retrieve data from a domain other than one from which the application is downloaded, you can set the url
as an absolute address This statement sets url to an absolute location on the Web:
The network resource to which you send an HTTPService request can be either a static text file
or a dynamic application server page that generates a response upon demand As long as the response is in a form that the HTTPService component is able to parse (usually a well-formed XML file), the response will be read and understood when it’s received from the server
Trang 5Setting the method property
The HTTPService component’s method property supports the following values:
l GET (the default)
l POST
l PUT (only with AIR or a proxy service)
l DELETE (only with AIR or a proxy service)
l OPTIONS (only with a proxy service)
l HEAD (only with a proxy service)
l TRACE (only with a proxy service)
In RPC-style applications, the HTTPService component is mostly used with the GET and POST
methods, while REST-style approaches sometimes use PUT and DELETE requests
Caution
Flash Player 10 only supports HTTP requests with methods of GET and POST Desktop applications deployed with AIR also can use the PUT and DELETE request methods To use PUT and DELETE requests in a Web application, or any other request methods, you must send requests through a server-side proxy such as the Proxy Service provided by LiveCycle Data Services and BlazeDS (described in Chapter 24) n
For example, Flex developers who use Ruby on Rails as their application server sometimes follow a RESTful pattern where the HTTPService method determines what kind of data manipulation is being requested by the client application Each of the following methods is treated as a “verb” that indicates what should be done with the data passed in the request:
l DELETE request Results in deleting existing data in the server tier.
l GET request Retrieves a representation of data without making any changes to the
ver-sion in the server’s persistent data store
l POST request Results in creating a new data item in the server tier.
l PUT request Results in modifying existing data in the server tier.
Trang 6Setting the resultFormat property
The resultFormat property determines how data is exposed in the Flex application when it’s received from the server The possible values are listed here:
l array The top-level object is returned as the first item in an ActionScript Array
l e4x Well-formed XML is returned as an ActionScript XML object that can be parsed and modified with EcmaScript for XML (E4X) syntax
l flashvars Data formatted as name/value pairs is parsed into an ActionScript Object
with named properties For example, the following String value is a well-formed
flashvars value:
firstName=Joe&lastName=Smith
l object (the default) Well-formed XML is returned as a tree of ActionScript objects
When a single element exists in a particular level of the XML hierarchy with a particular name, it’s returned as an instance of the ObjectProxy class; when multiple elements of the same name are returned, they’re wrapped in an ArrayCollection
l text The response is returned as a simple String value
l xml Well-formed XML is returned as an ActionScript XMLNode object that can be parsed with Document Object Model (DOM) code
The resulting ActionScript Object would have two properties named firstName and
lastName
Cross-Reference
The use of E4X to parse well-formed XML is described in Chapter 24 n
Setting the concurrency property
The concurrency property is implemented only with the MXML version of the HTTPService
component and determines how the responses from multiple concurrent requests will be handled
The property’s possible values are listed here:
l last Issuing another request before the last one was completed results in canceling the first request
l multiple (the default) Multiple responses are handled as they’re received from the
server, and it’s up to you (the developer) to create a code pattern that enables you to tify the responses for each request The AsyncToken class, an instance of which is returned from the send() method, can be helpful in this circumstance
iden-l single You can have only a single request active at any given time Issuing another request before the last one was completed results in a runtime error
The following code results in canceling any pending HTTPService requests when a new request
is sent:
<s:HTTPService id=”myService”
url=”data/contacts.xml”
concurrency=”last”/>
Trang 7Sending and Receiving Data
You send an HTTP request with the HTTPRequest object’s send() method For example, if you want to retrieve data upon application startup, you can call the send() method in the applica-tion’s creationComplete event handler:
Understanding asynchronous communications
All the Flex framework’s RPC components send and receive data asynchronously This means that when you send a request, Flash Player’s ActionScript Virtual Machine (AVM) doesn’t pause in its code execution and wait for data to be returned This architecture is similar to how a Web brows-er’s XMLHttpRequest object handles JavaScript requests for data: Requests are sent, and the responses are handled through event listeners
For ColdFusion developers, Flex’s HTTPService and ColdFusion’s <cfhttp> tags behave ferently ColdFusion handles responses to its <cfhttp> command synchronously, meaning that it waits for data to be returned before going to the next line of code Two major differences between the runtime environments account for this
dif-First, ColdFusion pages are transient and stay in server memory only until they’ve generated and returned HTML to the requesting Web browser Asynchronous operations require a runtime envi-ronment that stays in memory and can listen for a response Also, ColdFusion is multithreaded and can afford to allocate a thread to wait for a response Flash Player is single-threaded; if it had to wait for a response, the application would have to suspend all other operations such as animations and user interactions until the data came back
Handling HTTPService responses
You can handle the response from a server with two approaches:
Trang 8l With a binding expression that references returned data
l With event listeners that execute ActionScript code when data is returned
Of these approaches, the binding expression is simpler and easier to code, but it gives much less flexibility and power in terms of how you handle the returned data In contrast, the event listener architecture gives you the opportunity to debug, inspect, manipulate, and save returned data persistently
Using a binding expression
The HTTPService component’s lastResult property is a reference variable that gives you access to the data that’s returned from the server When the service object’s resultFormat prop-erty is set to the default value of object and you retrieve well-formed XML, the expression
myService.lastResult refers to an instance of the ObjectProxy class that represents the XML document
The following code represents the contents of an XML file named contacts.xml:
visu-lastResult property and then “walks” down the XML hierarchy to the repeating element name
The following DataGrid component uses the content of the repeating <row> elements as its data provider:
<mx:DataGrid dataProvider=”{contactService.lastResult.data.row}”/>
Try these steps in the chapter23 project:
1 Open contacts.xml file from the project’s src/data folder Notice that the XML
file has a root element named <contacts> and repeating elements named <row> Each
<row> element has a consistent internal structure consisting of named properties for tactId, firstname, lastname, and so on
Trang 92 Create a new MXML application named HTTPServiceWithBindings.mxml and
view it in Source mode
3 Set its layout property to an instance of <s:VerticalLayout> with
horizontalAlign set to center.
<s:layout>
<s:VerticalLayout horizontalAlign=”center”/>
</s:layout>
4 Add an <fx:Declarations> element after the closing </s:layout> tag.
5 Add an <s:HTTPService> tag set between the <fx:Declarations> tags Set its
id to contactService and its url property to data/contacts.xml:
<s:HTTPService id=”contactService”
url=”data/contacts.xml”/>
6 Add an <s:Button> component below the <s:HTTPService> tag Set its label to Get
Data and its click event listener to call the HTTPService object’s send() method:
<s:Button label=”Get Data” click=”contactService.send()”/>
7 Add a DataGrid component below the <mx:Button> tag Set its dataProvider to
display the HTTPService component’s returned data using a binding expression that references the XML file’s repeating <row> elements:
<mx:DataGrid dataProvider=”{contactService.lastResult.contacts.row}”/>
8 Run the application, and click the Get Data button to send the request You should
see the XML file’s data displayed in the DataGrid, as shown in Figure 23.10
The completed application is shown in Listing 23.3
</s:Application>
Trang 10FIGURE 23.10
Data retrieved from the XML file and displayed in the DataGrid
On the Web
The code in Listing 23.3 is available in the Web site files as HTTPWithBindingsComplete.mxml in the src
folder of the chapter23 project n
Tip
When you retrieve content from the local hard disk instead of a Web server, a file access runtime error might occur To fix this issue, you can place the application in a local security sandbox and block network access You do this in the project properties by adding the following compiler argument to the Flex project’s compiler arguments:
Figure 23.11 shows the Flex Compiler section of the project properties screen with the additional
-use-network compiler argument
Handling the result event
When data is returned from the remote server, the HTTPService object dispatches a result
event, whose event object is typed as mx.rpc.events.ResultEvent This ResultEvent
class has a result property that refers to the returned data
Trang 11FIGURE 23.11
Placing an application in the local sandbox to guarantee access to local files
Using local sandbox
Note
The ResultEvent class also has a headers property that in theory should return the HTTP response headers from the Web server In practice, this object frequently returns null n
To handle and save data using the result event, follow these steps:
1 Declare a bindable variable outside of any function that acts as a persistent
refer-ence to the returned data If you’re expecting a set of repeating data elements, cast the
variable as an ArrayCollection:
import mx.collections.ArrayCollection;
[Bindable]
private var myData:ArrayCollection
2 Create an event handler function that will be called when the event is dispatched The
function should receive a single event argument typed as ResultEvent and return void:
private function resultHandler(event:ResultEvent):void{
}
Trang 12You can use Flash Builder 4’s new event handler generation capabilities to create the handler function matically When you auto-complete the result event in the HTTPService MXML declaration, Flash Builder prompts you to generate the result handler It then creates the event handler function with the method signa- ture described in the preceding step n
3 Within the event handler function, use the event.result expression to refer to the
data that’s returned from the server Walk down the XML hierarchy to get to the
repeating data elements, and return that expression to the bindable ArrayCollection
variable:
myData = event.result.contacts.row;
You can listen for the result event with either an MXML attribute-based event listener or a call to the ActionScript addEventListener() method The attribute-based event listener looks like this:
The application in Listing 23.4 retrieves a data set at runtime using an HTTPService object’s
result event Data is saved to a persistent ArrayCollection variable that’s been marked as
[Bindable] and then displayed in a DataGrid using a binding expression
Trang 13protected function contactService_resultHandler(
event:ResultEvent):void {
myData = event.result.contacts.row;
} ]]>
It may seem at first glance that the use of the result event simply takes more code than a binding expression
Many advantages to this approach, however, make the extra code worthwhile By processing the returned data
in an event handler function, you have the opportunity to debug or modify data when it’s returned to the server, and the persistent variable enables you to refer to the data at any later point.
It’s also possible for a single service to return different data structures depending on which parameters are sent
in the request In this case, binding directly to the results isn’t possible, because you have to extract data from the result with expressions that can differ depending on the circumstance n
Handling the fault event
When an HTTPService request results in an error, the HTTPRequest object dispatches a
fault event, whose event object is typed as mx.rpc.events.FaultEvent This event object has a fault property typed as mx.rpc.Fault, which has these properties:
l faultCode:String A code that indicates the nature of the fault and whether it occurred in the client or server environment
l faultDetail:String An additional message that sometimes contains useful information
l message:String A string consisting of all the previous values concatenated together with | characters used as separators
When you debug a fault event, you can easily see the structure of the event object Figure 23.12 shows the Variables view during a debugging session showing the structure of the
FaultEvent and Fault objects
Trang 14FIGURE 23.12
The Variables view displaying fault information during a debugging session
To handle a fault event, create an event handler function that receives an event argument typed
as FaultEvent Within the body of the function, you can deal with the fault however you like
This suppresses the ugly error message that appears in response to unhandled faults in the debug player The following code collects fault information from the event object and displays it to the user with an Alert pop-up window:
private function faultHandler(event:FaultEvent):void{
Alert.show(event.fault.faultString, event.fault.faultCode);
}
Figure 23.13 shows the resulting application with the Alert dialog box showing the user the
faultString and faultCode values
As with the result event, you can listen for the fault event with either an MXML based event listener or the addEventListener() method The MXML attribute version looks like this:
Trang 15var myService:HTTPService = new HTTPService();
myService.url = “data/contacts.xml”;
myService.addEventListener(ResultEvent.RESULT, resultHandler);
myService.addEventListener(FaultEvent.FAULT, faultHandler);
FIGURE 23.13
Displaying fault information to the user
The application in Listing 23.5 shows the use of the fault event with an MXML-based event listener
Trang 16LISTING 23.5 (continued)
{ myData = event.result.contacts.xml;
} private function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.faultString, event.fault.faultCode);
} ]]>
Note
The result and fault events work exactly the same for all RPC components and use the same set of event classes, ResultEvent and FaultEvent The only significant difference lies in the structure of the data returned in a result event For example, HTTPService, when used to retrieve XML, returns data as a set of objects or as an E4X-compatible XML object, depending on the value of its resultFormat property In con- trast, WebService and RemoteObject return data based on data types declared in metadata returned from the server In all cases, though, you access the returned data by referencing the ResultEvent object’s
result property n
Working with CallResponder and AsyncToken
The CallResponder class is designed to enable you to assign multiple result and event handlers for different application scenarios Instead of binding a single set of event handlers to the RPC component, you assign the handlers to a particular CallResponder object:
Trang 17<s:HTTPService id=”contactService” url=”data/contacts.xml”/>
Note
As shown in the first section of this chapter on data-centric code generation, the CallResponder and
AsyncToken can also be used to set up a data binding relationship with a visual control After associating the
CallResponder with the call by assigning its token to the value returned from the service call, you can then refer to the CallResponder object’s lastResult property in a binding expression:
<mx:DataGrid id=”dataGrid”
The AsyncToken class is a dynamic object that enables you to add arbitrary named properties to the object at runtime and associate a remote server call with a responder object Every request to a remote operation through an RPC component returns an instance of AsyncToken that stays in application memory for the duration of the remote request After making the request, you can add as many bits of information as you need to track the purpose of the request or other important details:
var token:AsyncToken = contactService.send();
token.myProp1 = “Any property value”;
When data is returned in the result or fault event handlers, you can retrieve the arbitrary properties from the event object’s token property:
protected function resultHandler(event:ResultEvent):void{
var savedProp = event.token.myProp1;
}Using AsyncToken, you can “remember” information about the asynchronous call between the time it’s made and the time its data is returned or a fault is thrown
Using the AsyncToken Class
Trang 18The application in Listing 23.6 uses a CallResponder object to declare a pair of result and event handlers After the HTTPService object’s send() method is called, the application then binds the responder to the call by adding the responder to the returned AsyncToken The result event handler is successfully called when the data is returned, and the data is captured and displayed.
getDataResponder.token = contactService.send();
} protected function getData_resultHandler(event:ResultEvent):void {
myData = event.result.contacts.row;
} protected function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.faultString, event.fault.faultCode);
} ]]>
Trang 19On the Web
The code in Listing 23.6 is available in the Web site files as CallResponderDemo.mxml in the src folder of the chapter23 project n
Working with ItemResponder and AsyncToken
Developers who prefer to work entirely with ActionScript to manage their service calls sometimes use a pattern that includes the Flex framework’s ItemResponder and AsyncToken classes
Note
The ItemResponder and AsyncToken classes work nearly exactly the same with all RPC components n
The ItemResponder class can be used instead of the CallResponder, attribute-based event listeners, or the addEventListener() method to handle and dispatch event objects to ActionScript event handler functions To use ItemResponder, you first create custom event han-dler functions to handle an RPC request’s result and fault events Each event handler function receives an AsyncToken argument in addition to the expected event object For example, the
result handler function signature looks like this:
private function resultHandler(event:ResultEvent, token:AsyncToken):void
{ handle returned data
}
The fault event handler function looks like this:
private function faultHandler(event:FaultEvent, token:AsyncToken):void
{ handle fault
}
Before you make the RPC request, you create an instance of the ItemResponder class and pass references to the result and fault event handler functions as constructor method arguments:
var responder:ItemResponder = new ItemResponder(resultHandler,faultHandler);
Note
As with addEventListener(), you’re passing the functions as objects, and not calling them directly, so you only pass the function names and not their complete calling syntax n
The next steps are as follows:
1 Make the RPC request and return an instance of AsyncToken.
Trang 202 Add the ItemResponder object to the AsyncToken object’s array of responders
with its addResponder() method.
var token:AsyncToken = contactService.send();
token.addResponder(responder);
When the asynchronous request is completed, the AsyncToken object calls the appropriate event handler function, depending on whether a result or fault event is dispatched by the RPC component The event handler function receives both its event object and a reference to the
AsyncToken object that called it
The application in Listing 23.7 uses ItemResponder and AsyncToken objects to manage an asynchronous request and its result and fault events
var token:AsyncToken = contactService.send();
token.addResponder(responder);
} private function resultHandler(event:ResultEvent, token:AsyncToken):void
{
Trang 21myData = event.result.contacts.row;
} private function faultHandler(event:FaultEvent, token:AsyncToken):void {
Alert.show(event.fault.faultString, event.fault.faultCode);
} ]]>
as execute(), and its event handler methods, such as resultHandler() and faultHandler() Known
as the Command design pattern, this approach enables you to manage complex applications with dozens or thousands of unique server requests and is at the heart of the Cairngorm microarchitecture that’s used by developers of large Flex applications n
Web Resource
Steven Webster provides an excellent description of the Command design pattern (in the context of the Cairngorm microarchitecture) in his six-part series on Cairngorm at www.adobe.com/devnet/flex/
Working with Value Objects
XML-formatted data retrieved with the HTTPService component is always exposed as an
ArrayCollection of ObjectProxy instances If you prefer to work with strongly typed value object classes, you can create a simple set of code that transforms the Object instances into your value objects This process has two steps:
1 Create a value object class with the appropriate properties and set the value object
class’s constructor method to accept an optional argument typed as the ActionScript
Object class Within the constructor method, if the Object argument was passed in, transfer its property values to the equivalent properties in the current instance of the value object class:
Trang 22public function Contact(data:Object = null){
if (data != null) {
2 At runtime, in the result event handler, loop through the ArrayCollection and
create one new instance of the value object for each data item, and then replace the original object with the ArrayCollection class’s setItemAt() method:
private function resultHandler(event:ResultEvent):void{
Note
Notice that the value object class’s constructor method explicitly typecasts properties as necessary This is a major benefit of creating the extra code to transfer data from generic instances of the Object class to strongly typed value objects: Data is correctly cast and easier to use in other code throughout the application n
The application in Listing 23.8 retrieves data from the server and then loops through the
ArrayCollection to replace each generic Object with an equivalent value object When the user selects a row from the DataGrid, a bindable instance of the value object named current-Contact is filled in with the selected data and its details are displayed in the Panel
Trang 23import mx.collections.ArrayCollection;
import mx.events.ListEvent;
import mx.rpc.events.ResultEvent;
import vo.Contact;
[Bindable]
private var myData:ArrayCollection [Bindable]
private var currentContact:Contact;
private function contactService_resultHandler(event:ResultEvent):void {
currentContact = event.target.selectedItem as Contact;
} ]]>
<mx:DataGridColumn dataField=”firstname” headerText=”First Name”/>
<mx:DataGridColumn dataField=”lastname” headerText=”Last Name”/>
Trang 24On the Web
The code in Listing 23.8 is available in the Web site files as HTTPValueObjects.mxml in the src folder of the
chapter23 project The Contact value object class is defined in Contact.as in the src/vo subfolder n
Passing Parameters to Server Pages
When you use the HTTPService component to make calls to dynamic pages that are managed
by an application server, you frequently need to pass parameters The syntax for passing eters is the same regardless of whether you use the HTTPService component with GET or
param-POST requests
You can pass parameters in an HTTPService request two ways:
l Named parameters that are packaged in an ActionScript Object
l Bound parameters that are set up in the HTTPService object declaration
Using named parameters
To pass parameters by name, first create an instance of an ActionScript Object The Object
class is dynamic, meaning that you can add arbitrary named properties at runtime Set each eter as a named property of the properties object, and then pass the properties object as the only argument in the object’s send() method
param-The ActionScript code to accomplish these tasks might look like this:
private function sendData():void{
var params:Object = new Object();
params.firstname=”Joe”;
params.lastname=”Smith”;
contactService.send(params);
}
You also can use shorthand ActionScript code to create an object and pass it in a single statement:
private function sendData():void{
Trang 25In contrast, if the HTTPService object’s method property is set to POST, the parameters are appended to the end of the HTTP request The following is a literal POST request header sent from
a Flex application hosted by Microsoft Internet Explorer 6:
POST / HTTP/1.1Accept: */*
Accept-Language: en-USReferer: file://C:\flex4bible\workspace\chapter23\
bin-debug\HTTPSendParams.swfx-flash-version: 9,0,115,0Content-Type: application/x-www-form-urlencodedContent-Length: 28
Accept-Encoding: gzip, deflateUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;
.NET CLR 1.1.4322; NET CLR 2.0.50727; NET CLR 3.0.04506.648; NET CLR 3.5.21022)
Host: localhostConnection: Keep-AliveCache-Control: no-cachefirstname=Joe&lastname=Smith
Using bound parameters
You can set up bound parameters in an HTTPService declaration so that named properties are sent with a consistent source For example, assume that you’ve declared a bindable instance of a value object that stores current values:
[Bindable]
private var myContact:Contact;
If you know that the parameters you send with HTTPService always get their values from this object, you can declare the relationship using binding expressions:
<s:HTTPService id=”contactService” url=”myAppPage.php”
Cross-Reference
To fully demonstrate the use of HTTPRequest parameters, you need an application server that can receive and respond to parameterized requests n