03.14Tutorial - Consuming REST web services in ActionScript 3 - Part 2
Hi Everyone,
the first part of this tutorial can be found here: Tutorial - Consuming REST web services in ActionScript 3 - Part 1.
Today I will continue on the exploration of consuming REST web services in ActionScript 3. We will cover possible errors first, then create a versatile web service client object which is easily adaptable to all your projects.
IMPORTANT NOTE
Yesterday in my exploration of the new YouTube Data API roll-out, I ran into a wall concerning PUT and DELETE requests. The method described in these tutorials doesn't permit those operations because of limitations in the URLRequest object. It actually throws exceptions if you try to put the .method parameter to anything other than GET or POST. I find this ridiculous. Completely ridiculous. The method of the HTTP Request is a simple string in the HTTP header, 3 or 4 characters long. Why not give us control over it? For Flex users, you can use the HTTPService class, but Flash users? Left to nada. I prefer not using Flex, it's BLOATED. More on that in another post..
Where we were
At the end of the last tutorial, we had the following code:
-
// we are still sending GET variables along..
-
var request:URLRequest = new URLRequest("http://www.mydomain.com/feed/myfeed?query=youlike&start=34");
-
-
var loader:URLLoader = new URLLoader();
-
loader.dataFormat = URLLoaderDataFormat.TEXT;
-
request.method = URLRequestMethod.POST;
-
-
var variables:URLVariables = new URLVariables();
-
// these can be anything, you choose!
-
variables.vote = "yeah!";
-
variables.id = 2193;
-
-
// assign the data to be sent by POST
-
request.data = variables;
-
-
// add event listener
-
loader.addEventListener(Event.COMPLETE, handleResults);
-
-
// send the request
-
loader.load(request);
Then for the handler function, where we converted the results to XML
-
function handleResults(evt:Event):void
-
{
-
var response:String = evt.target.data as String;
-
var xmlData:XML;
-
try
-
{
-
xmlData = new XML(response);
-
}
-
catch(error:TypeError)
-
{
-
trace("the response data was not in valid XML format");
-
}
-
}
.
Handling Errors
So how many events do the URLLoader throw? And what do they do? Here is a summary :
- "complete"
flash.events.Event.COMPLETE- Dispatched after all the received data is decoded and placed in the data property of the URLLoader object. - "httpStatus"
flash.events.HTTPStatusEvent.HTTP_STATUS- Dispatched if a call to URLLoader.load() attempts to access data over HTTP. - "ioError"
flash.events.IOErrorEvent.IO_ERROR- Dispatched if a call to URLLoader.load() results in a fatal error that terminates the download. - "open"
Flash.events.Event.OPEN- Dispatched when the download operation commences following a call to the URLLoader.load() method. - "progress"
flash.events.ProgressEvent.PROGRESS- Dispatched when data is received as the download operation progresses. - "securityError"
flash.events.SecurityErrorEvent.SECURITY_ERROR- Dispatched if a call to URLLoader.load() attempts to load data from a server outside the security sandbox.
I am mostly interested in "complete", "ioError", "securityError", and eventually for requests which return anything other than 200 (201 Created is one example), we need to catch the "httpStatus". So let's handle those new events..
-
// add the imports at the beginning
-
import flash.events.IOErrorEvent;
-
import flash.events.SecurityErrorEvent;
-
-
// ...
-
-
// "event"
-
loader.addEventListener(Event.COMPLETE, handleResults);
-
-
// "ioError"
-
loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
-
-
// "securityError"
-
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR , handleSecurityError);
-
-
// send the request
-
loader.load(request);
For these new error event handlers, we implement the functions. What you do exactly inside those functions depends on the situation, you might want to display an error message, notify another service of the problem, or simply trace them to your output if you are in testing phase. This is what I will demonstrate now.
-
protected function handleIOError(evt:IOErrorEvent):void
-
{
-
trace("handleIOError(" + evt.toString());
-
}
-
-
protected function handleSecurityError(evt:SecurityErrorEvent):void
-
{
-
trace("handleSecurityError("+evt.toString());
-
}
On with the Singleton
The idea of the Singleton design pattern is to create an object that can be easily accessible from anywhere in your code. You import it, then you do :
-
var mySingleton = MySingleton.getInstance();
and off you go. You will always deal with the same object as this pattern ensures you only have one instance of it. In my head a little bulb lights up and says: this is what you need! Here is the code we need to create our singleton:
-
package webservice
-
{
-
import flash.net.URLRequestMethod;
-
import flash.net.URLVariables;
-
-
import flash.events.Event;
-
import flash.net.URLLoader;
-
import flash.net.URLRequest;
-
import flash.net.URLLoaderDataFormat;
-
import flash.events.IOErrorEvent;
-
import flash.events.SecurityErrorEvent;
-
-
public class WSClient
-
{
-
private static var _instance:YouTubeClient;
-
-
public static function getInstance():YouTubeClient
-
{
-
if (_instance == null)
-
_instance = new YouTubeClient();
-
-
return _instance;
-
}
-
-
public function WSClient()
-
{
-
// do nothing for now..
-
}
-
-
public function runRequest(query:String, start:Number):void
-
{
-
// code for our request goes here..
-
}
-
-
function handleResults(evt:Event):void
-
{
-
// result handling code goes here
-
}
-
-
protected function handleIOError(evt:IOErrorEvent):void
-
{
-
// ioerror handling code goes here
-
}
-
-
protected function handleSecurityError(evt:SecurityErrorEvent):void
-
{
-
// securityerror handling code goes here
-
}
-
}
-
}
Now, it is impossible to have a private or protected constructor. Hence some people came up with nifty tricks to enfore the Singleton; personally I work alone on most projects and don't need to. You can search on google for "as3 singleton" and you'll find good examples.
To use the WSClient we would do
-
import webservice.WSClient;
-
-
var client:WSClient = WSClient.getInstance();
-
-
client.runRequest();
But then you might ask, how does my calling code know when the results are ready?
Dispatching Events when the data comes in..
Once the data is handled by our handleResults function, it would be ideal if the WSClient would dispatch an event, telling the calling code that the results are ready to be used. We will therefore derive from EventDispatcher (some people prefer composition) this way:
-
package webservice
-
{
-
// other imports go here...
-
-
import flash.events.EventDispatcher;
-
-
public class WSClient extends EventDispatcher
-
{
-
// class implementation goes here..
-
}
-
}
Now for the dispatching of the event:
-
// inside of the handleResults function
-
function handleResults(evt:Event):void
-
{
-
var response:String = evt.target.data as String;
-
var xmlData:XML;
-
try
-
{
-
xmlData = new XML(response);
-
-
// we dispatch an event to notify the results are in..
-
dispatchEvent(new Event("results", false, false));
-
}
-
catch(error:TypeError)
-
{
-
trace("the response data was not in valid XML format");
-
}
-
}
Our caller code can now be aware that the data is ready, but how to access it?
Custom Events
The approach I like is to create custom event classes. One such event class is used to notify the caller code that the data is ready and transport the data to it. To do this, we need to derive from the flash.event.Event class. We will define one event type, add a property to contain the data, and define a getter to access it:
-
package events
-
{
-
import flash.events.Event;
-
-
public class ResultEvent extends Event
-
{
-
public static const RESULTS_RECEIVED:String = "results_received";
-
-
// in our case the data is of XML type..
-
protected var _data:XML;
-
-
// the getter
-
public function get data():XML
-
{
-
return _data;
-
}
-
-
public function ResultEvent(type:String, data:XML)
-
{
-
// initialize the event
-
super(type);
-
-
// keep reference to the data
-
_data = data;
-
}
-
}
-
}
On with dispatching the custom event:
-
// import it
-
import events.ResultEvent;
-
-
// inside of the handleResults function
-
function handleResults(evt:Event):void
-
{
-
var response:String = evt.target.data as String;
-
var xmlData:XML;
-
try
-
{
-
xmlData = new XML(response);
-
-
// we dispatch the custom event which will carry the data for us to all listeners..
-
dispatchEvent(new ResultEvent(ResultEvent.RESULTS_RECEIVED, xmlData));
-
}
-
catch(error:TypeError)
-
{
-
trace("the response data was not in valid XML format");
-
}
-
}
Back to the caller code:
-
import webservice.WSClient;
-
import events.ResultEvent;
-
-
var client:WSClient = WSClient.getInstance();
-
client.addEventListener(ResultEvent.RESULTS_RECEIVED, processResult);
-
client.runRequest("beauty", 10);
-
-
protected function processResult(evt:ResultEvent):void
-
{
-
// get our results..
-
var data:XML = evt.data;
-
-
// do something with it!
-
}
Looks like there will be a third installment! I will describe how to handle situations were multiple callers use the same function: how to ensure the results you are getting are yours and not the results from somebody else's request. I will also standardize certain tasks inside of the WSClient.
Cheers!
Martin
Related posts (automatically generated):
- Tutorial - Consuming REST web services in ActionScript 3 - Part 1
- Tutorial - Consuming REST web services in ActionScript 3 - Part 3
- Tutorial - AS3 & REST web services with RESTProxy - Part 1
- Tutorial - Consuming REST web services in ActionScript 3 - Part 4

[...] the next installment, I demonstrate how I wrap all this functionality in a [...]
March 14th, 2008 at 8:57 am
very good I waiting for this long time ago and I cannot wait for the third part, thanks!!!
April 30th, 2008 at 4:15 pm
[...] This is the third installment of the series, you can find the first two here: Part 1 - http://blog.martinlegris.com/?p=87 Part 2 - http://blog.martinlegris.com/?p=90 [...]
May 31st, 2008 at 12:36 pm
Any news on the rare HTTPService/URLRequest implementation and other HTTP methods like PUT/HEAD? Do you know if something changed with Flash Player 10? I could find a bug report on Adobe’s JIRA yet.
October 15th, 2008 at 10:28 am
Thanks for this, it is great! But I’m stuck. Maybe I’m just not getting it, but what is the YouTubeClient() all about? I assume it has to do with the YouTube Data API.
What if I wasn’t using that, what would I put there instead?
Thanks, James
November 23rd, 2009 at 1:41 pm
Disregard the previous message! I’m an idiot.
November 23rd, 2009 at 2:18 pm
You could always use the HTTP Service class from inside Flash.
http://www.jeffguthrie.com/httpservice-and-arraycollection-in-flash-cs4
I like this tutorial though… Very helpful.
February 4th, 2010 at 5:00 pm
Is there a sourcecode example available? I am trying to implement a webservice call in AS3. Thnks in advance
March 22nd, 2010 at 2:09 pm