- Facebook Graph API Development with Flash
- Michael James Williams
- 965字
- 2025-03-31 05:39:04
Time for action - creating an HTTP Requestor
The idea is, we move all of the code regarding the URLLoader
from CustomGraphContainerController
to a separate class, called HTTPRequestor
. We will then replace the CustomGraphContainerController
constructor with this:
public function CustomGraphContainerController(a_graphControlContainer:GraphControlContainer)
{
super(a_graphControlContainer);
_requestor = new HTTPRequestor();
_requestor.request(new GraphRequest("PacktPub"));
}
Why bother? Well, apart from being neater, there are two main advantages:
- It's much simpler to request several Graph Objects or Graph Lists; no need to deal with multiple instances of
URLLoader
. - In the next chapter, we'll see how to use the official Adobe AS3 Facebook SDK to retrieve information from the Graph API. If all the code for a request is encapsulated in one class, then we only need to change one line to switch from using HTTP to using Adobe's SDK:
public function CustomGraphContainerController(a_graphControlContainer:GraphControlContainer) { super(a_graphControlContainer); _requestor = new SDKRequestor(); _requestor.request(new GraphRequest("PacktPub")); }
Note
GraphRequest
is a simple class; its constructor allows you to use two parameters to specify what you'd like to retrieve from the Graph API:
objectID
, the name of any Graph Object.connectionID
, the name of any connection of that Graph Object.
So, to request the Packt Publishing Page, you would use this GraphRequest:
newGraphRequest("PacktPub")
;
and to request the list of Posts from the Packt Publishing Page, you'd use this:
newGraphRequest("PacktPub", "posts")
;
The class is already written; it's in \src\com\graph\apis\http\HTTPRequestor.as
. Take a look! There are a few changes from the code we wrote in CustomGraphContainerController.as
, but these all have comments to explain them:
package graph.apis.http { import events.DialogEvent; import events.RequestEvent; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.HTTPStatusEvent; import flash.events.IEventDispatcher; import flash.events.IOErrorEvent; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.URLVariables; import flash.utils.Dictionary; import graph.apis.base.IRequestor; import graph.BaseGraphItem; import graph.GraphList; import graph.GraphObject; import graph.GraphRequest; import com.adobe.serialization.json.JSON; //the class needs to dispatch events (see later in code for why) public class HTTPRequestor extends EventDispatcher implements IRequestor { //this is used to figure out which GraphRequest created each //loader private var _requests:Dictionary = new Dictionary(); public function HTTPRequestor(target:IEventDispatcher = null) { //this is needed because the class extends EventDispatcher super(target); } public function request(a_request:GraphRequest):void { var loader:URLLoader = new URLLoader(); var urlRequest:URLRequest = new URLRequest(); var variables:URLVariables = new URLVariables(); //We construct a URL from the parameters of the GraphRequest urlRequest.url = "https://graph.facebook.com/" + a_request.objectID; if (a_request.connectionID) { urlRequest.url += "/" + a_request.connectionID; } variables.metadata = 1; urlRequest.data = variables; //this is used to figure out which GraphRequest created the loader later _requests[loader] = a_request; loader.addEventListener(Event.COMPLETE, onGraphDataLoadComplete); loader.load(urlRequest); } private function onGraphDataLoadComplete(a_event:Event):void { var loader:URLLoader = a_event.target as URLLoader; var graphData:String = loader.data; var decodedJSON:Object = JSON.decode(graphData); //we find the original GraphRequest used to start the loader var originalRequest:GraphRequest = _requests[loader] as GraphRequest; if (decodedJSON.data) { var graphList:GraphList = new GraphList(); var childGraphObject:GraphObject; for each (var childObject:Object in decodedJSON.data) { childGraphObject = new GraphObject(); for (var childKey:String in childObject) { childGraphObject[childKey] = childObject[childKey]; } graphList.addToList(childGraphObject); } graphList.paging = decodedJSON.paging; //we use the properties of the original GraphRequest to add //some extra data to the GraphList itself graphList.ownerID = originalRequest.objectID; graphList.connectionType = originalRequest.connectionID; //since this class does not have a renderGraphList() method, //we dispatch an event, which CustomGraphContainerController //will listen for, and call its own renderGraphList() method dispatchEvent(new RequestEvent(RequestEvent.REQUEST_COMPLETED, graphList)); } else { var graphObject:GraphObject = new GraphObject(); for (var key:String in decodedJSON) { graphObject[key] = decodedJSON[key]; } //since this class does not have a renderGraphList() method, //we dispatch an event, which CustomGraphContainerController //will listen for, and call its own renderGraphList() method dispatchEvent(new RequestEvent(RequestEvent.REQUEST_COMPLETED, graphObject)); } } } }
There's no need to change any of this, or even to understand any of it apart from the HTTP request code that we wrote earlier. Just remember, its purpose is to encapsulate your requests to the Graph API.
Now, go back to CustomGraphContainerController.as
and remove all the request-related code:
package controllers { import ui.GraphControlContainer; public class CustomGraphContainerController extends GCController { public function CustomGraphContainerController (a_graphControlContainer:GraphControlContainer) { super(a_graphControlContainer); } } }
CustomGraphContainerController
inherits a protected variable called _requestor
of type IRequestor
, as well as a method for adding the required event listeners to it, so all we need to do is this:
package controllers { import graph.apis.http.HTTPRequestor; import graph.GraphRequest; import ui.GraphControlContainer; public class CustomGraphContainerController extends GCController { public function CustomGraphContainerController (a_graphControlContainer:GraphControlContainer) { super(a_graphControlContainer); _requestor = new HTTPRequestor(); addEventListenersToRequestor(); _requestor.request(new GraphRequest("PacktPub")); } } }
Compile and run your SWF, then expand the Connections box and click on "posts":

Great! The Graph List Renderer appears, with a black line to the Page to indicate that there is a connection between them. What about the other connections? Try clicking on statuses.
Error #2044: Unhandled ioError:.text=Error #2032: Stream Error. URL: https://graph.facebook.com/204603129458/statuses?metadata=1
Oops.
What just happened?
If you load the troublesome URL in your browser (https://graph.facebook.com/packtpub/statuses), you'll see the following message:
{ "error": { "type": "OAuthAccessTokenException", "message": "An access token is required to request this resource." } }
This error is due to not being logged in to Facebook through your SWF. We'll look at how to solve this in the next chapter.
Note
For now, you can get around the error by adding an IO_ERROR
event listener to the URLLoader
. In HTTPRequestor.as
, modify request():
public function request(a_request:GraphRequest):void
{
varloader:URLLoader = new URLLoader();
varurlRequest:URLRequest = new URLRequest();
varvariables:URLVariables = new URLVariables();
//We construct a URL from the parameters of the //GraphRequest
urlRequest.url = "https://graph.facebook.com/" + a_request.objectID;
if (a_request.connectionID)
{
urlRequest.url += "/" + a_request.connectionID;
}
variables.metadata = 1;
urlRequest.data = variables;
//this is used to figure out which GraphRequest //created the loader later
_requests[loader] = a_request;
loader.addEventListener(Event.COMPLETE, onGraphDataLoadComplete);
loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
loader.load(urlRequest);
}
You will need to import flash.events.IOErrorEvent
. Now, in the same class, create a simple event handler function to trace the error:
private function onIOError(a_event:IOErrorEvent):void { trace(a_event.text); }