Archive for the ‘Adobe’ Category
The Netflix API Part 2 – Getting Authorization and Access
Please note that all keys, tokens and signatures in this series are made up and not usable.
Do not store your secret key in your application as swf files are not secure.
The first post in this series, The Netflix API Part 1 – Making Your Initial Request, was basically about OAuth and how to deal with it to get initial access to Netflix using your application. Thankfully we’ve got the AS3 OAuth library from iotashan.com to deal with it. However, it is necessary to understand the basic steps required for OAuth access to get going and it will help with many other mashable sites since it is a common way of gaining user authorization to protected assets.
Of course I’ve reworked ALL of the code from the last example but I won’t be as explicit in explaining what I covered in the last section. Call it an Iteration.
It is now a Flex example so you Flash people will have to adjust. Shouldn’t be too hard. Use URLLoader instead of HTTPService.
I’ve created a single MXML file for the project called NetflixAPITutorial.mxml.
The main imports and static variables from the previous section are as follows:
import flash.net.navigateToURL; import mx.rpc.http.HTTPService; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import org.iotashan.oauth.*; private static const CONSUMER_KEY:String = "INSERT YOUR CONSUMER KEY HERE"; private static const CONSUMER_SECRET:String = "INSERT YOUR CONSUMER SECRET HERE" private static const NETFLIX_BASE_URL:String = "http://api.netflix.com/"; private static const SIG_METHOD:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
We’ll also need for the items from the last tutorial such as consumer, service, and request token. We’ll also need variables for the authorization url, the access token and the user id.
private var _consumer:OAuthConsumer; private var _service:HTTPService; private var _requestToken:OAuthToken; private var _authUrl:String; private var _accessToken:OAuthToken; private var _userId:String;
Instead of using a bunch of different buttons, I’m going to use one button and change it’s function using a setButtonState method. It will get passed one of the following static state strings. These are in the order of what we’ll be doing in this tutorial.
- Getting an initial request token
- Sending the user to Netflix for authorization
- Asking the user if authorization is complete
- Getting user info
- Getting the user’s queue
private static const BUTTON_STATE_REQUEST_TOKEN:String = "request_token"; private static const BUTTON_STATE_GET_AUTHORIZATION:String = "get_authorization"; private static const BUTTON_STATE_AUTHORIZATION_COMPLETE:String = "authorization_complete"; private static const BUTTON_STATE_GET_USER_INFO:String = "get_user_info"; private static const BUTTON_STATE_GET_USER_QUEUE:String = "get_user_queue";
Here are the methods for requesting the access token (don’t forget to add onCreationComplete() to the creationComplete Attribute of your application component tag). This will get you to where we left off last time.
private function onCreationComplete():void
{
_consumer = new OAuthConsumer(NetflixAPITutorial.CONSUMER_KEY, NetflixAPITutorial.CONSUMER_SECRET);
setButtonState(NetflixAPITutorial.BUTTON_STATE_REQUEST_TOKEN);
}
private function getRequestToken_Click(event:MouseEvent):void
{
getRequestToken();
}
private function getRequestToken():void
{
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, requestTokenResult);
_service.addEventListener(FaultEvent.FAULT, requestTokenFault);
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
_service.url = tokenRequest.buildRequest(NetflixAPITutorial.SIG_METHOD);
_service.send();
}
private function requestTokenResult(event:ResultEvent):void
{
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_AUTHORIZATION);
debug(event.result as String);
}
private function requestTokenFault(event:FaultEvent):void
{
debug("requestTokenFault() " + event.fault.message);
}
private function setButtonState(state:String):void
{
switch(state)
{
case NetflixAPITutorial.BUTTON_STATE_REQUEST_TOKEN:
btnRequest.label = "Request Token";
btnRequest.addEventListener(MouseEvent.CLICK, getRequestToken_Click);
break;
}
}
private function debug(message:String):void
{
txtDebug.text += message + "\n\n";
}
And here are your TextArea and Button that go after the script tag:
<mx:TextArea id="txtDebug" width="100%" height="100%" /> <mx:Button id="btnRequest" />
Run the app and you’ll see that you get your token request.
Now the data that is returned is a new oauth_token key and a new oauth_token_secret along with an application name and an url to send the user to to request permission to access their data on Netflix. We want to create a new oauth request token with the new token key and secret. To send the user to Netflix to allow access we also need to append the application name and consumer key to the request. We’ll create a parseRequestToken method and call it from the requestTokenResult method.
Here is parseRequestToken:
private function parseTokenRequest(result:String):void
{
var aResult:Array = result.split("&");
var split:Array;
var oResult:Object = new Object();
for each(var item:String in aResult)
{
split = item.split("=");
oResult[split[0]] = split[1];
}
_requestToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
_loginUrl = unescape(oResult.login_url as String) + "&application_name=" + oResult.application_name + "&oauth_consumer_key=" + NetflixAPITutorial.CONSUMER_KEY;
}
It splits on the ampersand character and then parses the resulting array splitting on the equals (=) character. Since the returned values are known we can use these as property values on the oResult object and retrieve them to create and save the new requestToken and loginUrl with the application_name and oauth_consumer_key values appended to the query string.
The requestTokenResult with the parseTokenResult call now looks like this:
private function requestTokenResult(event:ResultEvent):void
{
parseTokenRequest(event.result as String);
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_AUTHORIZATION);
debug(event.result as String);
}
Getting Authorization from the User
First we’ll need to add the new button state case to the setButtonStateMethod:
case NetflixAPITutorial.BUTTON_STATE_GET_AUTHORIZATION: btnRequest.label = "Get User Authorization"; btnRequest.removeEventListener(MouseEvent.CLICK, getRequestToken_Click); btnRequest.addEventListener(MouseEvent.CLICK, getAuthorization_Click); break;
Now add the getAuthorization_Click, getAuthorization methods. We’ll also add another case to the getButtonState method. getAuthorization will take the user to the Netflix site for to authorize your application. When the user is finished they return to the application and select the “Authorization Complete” button.
private function getAuthorization_Click(event:MouseEvent):void
{
getAuthorization();
}
private function getAuthorization():void
{
var req:URLRequest = new URLRequest(_loginUrl);
navigateToURL(req);
setButtonState(NetflixAPITutorial.BUTTON_STATE_AUTHORIZATION_COMPLETE);
}
private function authorizationComplete_Click(event:MouseEvent):void
{
//code to come in the next section
}
Add this to the setButtonState method:
case NetflixAPITutorial.BUTTON_STATE_AUTHORIZATION_COMPLETE:btnRequest.label = "Authorization Complete";
btnRequest.removeEventListener(MouseEvent.CLICK, getAuthorization_Click);
btnRequest.addEventListener(MouseEvent.CLICK, authorizationComplete_Click);
break;
Go ahead and run this code (authorize your own Netflix account) and then we’ll get to what to do after the user grants authorization.
Requesting an Access Token
Now that the application has been authorized we need to request an access token.
Add getAccessToken() to the authorizationComplete method and add the getAccessToken method and call as well as it’s result, fault and parse methods as shown below.
private function getAuthorization():void
{
var req:URLRequest = new URLRequest(_loginUrl);
navigateToURL(req);
setButtonState(NetflixAPITutorial.BUTTON_STATE_AUTHORIZATION_COMPLETE);
}
private function authorizationComplete_Click(event:MouseEvent):void
{
getAccessToken();
}
private function getAccessToken():void
{
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, accessTokenResult);
_service.addEventListener(FaultEvent.FAULT, accessTokenFault);
var tokenRequest:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "oauth/access_token", null, _consumer, _requestToken);
_service.url = tokenRequest.buildRequest(NetflixAPITutorial.SIG_METHOD);
_service.send();
}
private function accessTokenResult(event:ResultEvent):void
{
debug(event.result as String);
parseAccessTokenRequest(event.result as String);
}
private function accessTokenFault(event:FaultEvent):void
{
debug("accessTokenFault() " + event.fault.message);
}
private function parseAccessTokenRequest(result:String):void
{
var aResult:Array = result.split("&");
var split:Array;
var oResult:Object = new Object();
for each(var item:String in aResult)
{
split = item.split("=");
oResult[split[0]] = split[1];
}
_accessToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
_userId = oResult.user_id;
}
Note that in the getAccessTokenMethod the new OAuthRequest call uses a path of “oauth/access_token” and uses the new _requestToken value. In the parseAccessTokenRequest method you can see that we use the returned oauth_token and oauth_token_secret to create a new OAuthToken and store it in the _accessToken property. We’ll also store the returned user_id value in the _userId property.
Run this code and check out the result and then we’ll finally get to the good stuff.
Getting User Information from Netflix
With the access token and the userID we can now really start to work with the user’s data on Netflix. Lets start by getting the user’s information.
First add a Get User Info button state to the setButtonStateMethod
case NetflixAPITutorial.BUTTON_STATE_GET_USER_INFO: btnRequest.label = "Get User Info"; btnRequest.removeEventListener(MouseEvent.CLICK, authorizationComplete_Click); btnRequest.addEventListener(MouseEvent.CLICK, getUserInfo_Click);
Add the setButtonStateMethod call to the accessTokenResult method.
private function accessTokenResult(event:ResultEvent):void
{
debug(event.result as String);
parseAccessTokenRequest(event.result as String);
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_USER_INFO);
}
Now we’ll fill out the authorizationComplete_Click method, add the authorizationComplete, getUserInfo, userInfoResult and userInfoFault methods.
private function getUserInfo_Click(event:MouseEvent):void
{
getUserInfo();
}
private function getUserInfo():void
{
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, userInfoResult);
_service.addEventListener(FaultEvent.FAULT, userInfoFault);
var req:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "users/" + _userId, null, _consumer, _accessToken);
_service.url = req.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);
_service.send();
}
private function userInfoResult(event:ResultEvent):void
{
debug("userInfoResult()\n\n" + event.result);
}
private function userInfoFault(event:FaultEvent):void
{
debug("userInfoFault() " + event.fault.message);
}
In the getUserInfoMethod you’ll see that when we create the OAuthRequest that we’re appending “users/” and the _userId value to the base url. Go ahead and run this code and see what you get.
You’ll see that you get “[object Object]” back. Let’s check this out by putting a breakpoint in the userInfoResult method. Run the app again. When you hit the break points check the event.result value. You’ll see that it’s of type ProxyObject and that you can see all the properties in there. The reality is that the returned value is actually XML. You can try to deal with the proxy object but trust me it won’t be fun. We can tell the HTTPService instance that we are going to get XML back by setting the the resultFormat property to E4X. getUserInfo will now look like this:
private function getUserQueue():void
{
_service = new HTTPService();
_service.resultFormat = "e4x"
_service.addEventListener(ResultEvent.RESULT, userQueueResult);
_service.addEventListener(FaultEvent.FAULT, userQueueFault);
var req:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "users/" + _userId + "/queues/disc", null, _consumer, _accessToken);
_service.url = req.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);
_service.send();
}
Remove the breakpoint, build and run again.
You’ll see some sweet XML printed to the debug field. Rock! This is something you can use. Of course I won’t tell you what to do with it but with the access token, your consumer key and the proper url, which you can find in the Netflix API documentation you’re sure to be laughing.
Getting the User’s Queue from Netflix
I won’t go into much detail on this but really all you need is to use the correct url in the OAuthRequest to get the queue. Add the button state and call, then the usual click, request, result and fault methods.
The setButtonState case:
case NetflixAPITutorial.BUTTON_STATE_GET_USER_QUEUE: btnRequest.label = "Get User Queue"; btnRequest.removeEventListener(MouseEvent.CLICK, getUserInfo_Click); btnRequest.addEventListener(MouseEvent.CLICK, getUserQueue_Click); break;
The rest of the methods:
private function getUserQueue_Click(event:MouseEvent):void
{
getUserQueue();
}
private function getUserQueue():void
{
_service = new HTTPService();
_service.resultFormat = "e4x"
_service.addEventListener(ResultEvent.RESULT, userQueueResult);
_service.addEventListener(FaultEvent.FAULT, userQueueFault);
var req:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "users/" + _userId + "/queues/disc", null, _consumer, _accessToken);
_service.url = req.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);
_service.send();
}
private function userQueueResult(event:ResultEvent):void
{
debug("userQueueResult(): \n\n" + event.result);
}
private function userQueueFault(event:FaultEvent):void
{
debug("userQueueFault(): " + event.fault.message);
}
In the next post in this series I’ll talk about how to deal with managing access for the users of your application.
Here’s all of the source code for this tutorial:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import flash.net.navigateToURL;
import mx.rpc.http.HTTPService;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import org.iotashan.oauth.*;
private static const CONSUMER_KEY:String = "[Insert your key here]";
private static const CONSUMER_SECRET:String = "[Insert your secret here]"
private static const NETFLIX_BASE_URL:String = "http://api.netflix.com/";
private static const SIG_METHOD:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
private var _consumer:OAuthConsumer;
private var _service:HTTPService;
private var _requestToken:OAuthToken;
private var _loginUrl:String;
private var _accessToken:OAuthToken;
private var _userId:String;
private static const BUTTON_STATE_REQUEST_TOKEN:String = "request_token";
private static const BUTTON_STATE_GET_AUTHORIZATION:String = "get_authorization";
private static const BUTTON_STATE_AUTHORIZATION_COMPLETE:String = "authorization_complete";
private static const BUTTON_STATE_GET_USER_INFO:String = "get_user_info";
private static const BUTTON_STATE_GET_USER_QUEUE:String = "get_user_queue";
private function onCreationComplete():void
{
_consumer = new OAuthConsumer(NetflixAPITutorial.CONSUMER_KEY, NetflixAPITutorial.CONSUMER_SECRET);
setButtonState(NetflixAPITutorial.BUTTON_STATE_REQUEST_TOKEN);
}
private function getRequestToken_Click(event:MouseEvent):void
{
getRequestToken();
}
private function getRequestToken():void
{
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, requestTokenResult);
_service.addEventListener(FaultEvent.FAULT, requestTokenFault);
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
_service.url = tokenRequest.buildRequest(NetflixAPITutorial.SIG_METHOD);
_service.send();
}
private function requestTokenResult(event:ResultEvent):void
{
parseTokenRequest(event.result as String);
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_AUTHORIZATION);
debug(event.result as String);
}
private function requestTokenFault(event:FaultEvent):void
{
debug("requestTokenFault() " + event.fault.message);
}
private function parseTokenRequest(result:String):void
{
var aResult:Array = result.split("&");
var split:Array;
var oResult:Object = new Object();
for each(var item:String in aResult)
{
split = item.split("=");
oResult[split[0]] = split[1];
}
_requestToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
_loginUrl = unescape(oResult.login_url as String) + "&application_name=" + oResult.application_name + "&oauth_consumer_key=" + NetflixAPITutorial.CONSUMER_KEY;
}
private function getAuthorization_Click(event:MouseEvent):void
{
getAuthorization();
}
private function getAuthorization():void
{
var req:URLRequest = new URLRequest(_loginUrl);
navigateToURL(req);
setButtonState(NetflixAPITutorial.BUTTON_STATE_AUTHORIZATION_COMPLETE);
}
private function authorizationComplete_Click(event:MouseEvent):void
{
authorizationComplete();
}
private function authorizationComplete():void
{
getAccessToken();
}
private function getAccessToken():void
{
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, accessTokenResult);
_service.addEventListener(FaultEvent.FAULT, accessTokenFault);
var tokenRequest:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "oauth/access_token", null, _consumer, _requestToken);
_service.url = tokenRequest.buildRequest(NetflixAPITutorial.SIG_METHOD);
_service.send();
}
private function accessTokenResult(event:ResultEvent):void
{
debug(event.result as String);
parseAccessTokenRequest(event.result as String);
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_USER_INFO);
}
private function accessTokenFault(event:FaultEvent):void
{
debug("accessTokenFault() " + event.fault.message);
}
private function parseAccessTokenRequest(result:String):void
{
var aResult:Array = result.split("&");
var split:Array;
var oResult:Object = new Object();
for each(var item:String in aResult)
{
split = item.split("=");
oResult[split[0]] = split[1];
}
_accessToken = new OAuthToken(oResult.oauth_token, oResult.oauth_token_secret);
_userId = oResult.user_id;
}
private function getUserInfo_Click(event:MouseEvent):void
{
getUserInfo();
}
private function getUserInfo():void
{
_service = new HTTPService();
_service.resultFormat = "e4x"
_service.addEventListener(ResultEvent.RESULT, userInfoResult);
_service.addEventListener(FaultEvent.FAULT, userInfoFault);
var req:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "users/" + _userId, null, _consumer, _accessToken);
_service.url = req.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);
_service.send();
}
private function userInfoResult(event:ResultEvent):void
{
debug("userInfoResult()\n\n" + event.result);
setButtonState(NetflixAPITutorial.BUTTON_STATE_GET_USER_QUEUE);
}
private function userInfoFault(event:FaultEvent):void
{
debug("userInfoFault() " + event.fault.message);
}
private function getUserQueue_Click(event:MouseEvent):void
{
getUserQueue();
}
private function getUserQueue():void
{
_service = new HTTPService();
_service.resultFormat = "e4x"
_service.addEventListener(ResultEvent.RESULT, userQueueResult);
_service.addEventListener(FaultEvent.FAULT, userQueueFault);
var req:OAuthRequest = new OAuthRequest("GET", NetflixAPITutorial.NETFLIX_BASE_URL + "users/" + _userId + "/queues/disc", null, _consumer, _accessToken);
_service.url = req.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);
_service.send();
}
private function userQueueResult(event:ResultEvent):void
{
debug("userQueueResult(): \n\n" + event.result);
}
private function userQueueFault(event:FaultEvent):void
{
debug("userQueueFault(): " + event.fault.message);
}
private function saveToken(token:OAuthToken, key:String):void
{
}
private function getToken(key:String):OAuthToken
{
return null;
}
private function setButtonState(state:String):void
{
switch(state)
{
case NetflixAPITutorial.BUTTON_STATE_REQUEST_TOKEN:
btnRequest.label = "Request Token";
btnRequest.addEventListener(MouseEvent.CLICK, getRequestToken_Click);
break;
case NetflixAPITutorial.BUTTON_STATE_GET_AUTHORIZATION:
btnRequest.label = "Get User Authorization";
btnRequest.removeEventListener(MouseEvent.CLICK, getRequestToken_Click);
btnRequest.addEventListener(MouseEvent.CLICK, getAuthorization_Click);
break;
case NetflixAPITutorial.BUTTON_STATE_AUTHORIZATION_COMPLETE:
btnRequest.label = "Authorization Complete";
btnRequest.removeEventListener(MouseEvent.CLICK, getAuthorization_Click);
btnRequest.addEventListener(MouseEvent.CLICK, authorizationComplete_Click);
break;
case NetflixAPITutorial.BUTTON_STATE_GET_USER_INFO:
btnRequest.label = "Get User Info";
btnRequest.removeEventListener(MouseEvent.CLICK, authorizationComplete_Click);
btnRequest.addEventListener(MouseEvent.CLICK, getUserInfo_Click);
break;
case NetflixAPITutorial.BUTTON_STATE_GET_USER_QUEUE:
btnRequest.label = "Get User Queue";
btnRequest.removeEventListener(MouseEvent.CLICK, getUserInfo_Click);
btnRequest.addEventListener(MouseEvent.CLICK, getUserQueue_Click);
break;
}
}
private function debug(message:String):void
{
txtDebug.text += message + "\n\n";
}
]]>
</mx:Script>
<mx:TextArea id="txtDebug" width="100%" height="100%" />
<mx:Button id="btnRequest" />
</mx:WindowedApplication>
Ah The Memories…Dev Abuse is Forever
My good friend Stacey Mulcahy just posted the first in a series about project management from a developer’s perspective in InsideRIA. The theme being, Bend Over Here It Comes Again. Surely much of this opinion was formed during our days at a certain interactive agency in Atlanta.
I’ve left the agency world and moved into RIA development. It’s a lot different so far here at EUI, but I’m sure that there are agencies all over the world whose developers are considered to be in the IT department rather than integrated with creative. In my opinion if you want to be efficient and create stellar work on the Flash Platform devs and creatives must work together!
To the Mile High City!
My goal of moving toward more app-centric development has come true! I’m moving to Denver for at least six months to take on a contract to hire opportunity. The adventure for me and new partner in crime Donnie begins tomorrow!
Things have been super stressful with me trying to get ready to move while working marathon hours but the time is here. I’m soooo ready to start this next step in my life! Thanks to all of my family and friends who have helped me out during the last few months of enormous change – you know who you are!
ActionScript Coder for Hire!
As of February, I am on the market for work coding Flash. I am well versed in AS2 and currently working on picking up AS3. I am looking for full time work but I will be glad to take contract as well. I have worked on sites such as:
whiteboard.ups.com
www.vwfeatures.com (GTI Joyride)
www.gptimemachine.com
onehundred.genworth.com
www.freedomoftheseas.com
I currently live in the Atlanta area.
If you are interested, contact me: brentpub [at] darbymedia {dot} com.
IE, Local Playback, FLVs and Querystrings
I had a problem today with a Flash site converted to playback on CD-ROM where it would work fine in Firefox but FLV content would not playback in IE.
It turns out that for the web version if bandwidth had not been detected and tracked that I was appending a ?nocache=[random number] to the filename in order to make sure that the file would load even if cached. On the CD-ROM (or running directly from the hard drive) this would cause a “stream not found” error. Disabling this so that a querystring was not used solved my problem.
Receiving Object Data With .NET/Fluorine/Flex
UPDATE: Please note that this post is from January of 2007. While it may help you in some respects, I have not been working with Fluorine so I will not be able to answer any questions. Also, any SWF embeds or links to working code are no longer valid. I have moved from a personally managed WordPress install to WordPress hosting so the formatting has been affected as well. MXML is not displaying at all and I’ll try to fix this in the near future.
So I went through the very basics last time, and while I’m gonna keep it simple, I’m not gonna do a newbie style step-by-step like I did before. For this tutorial I’m going to create a little media library sample for you to work with to show how typed object data can be received via Fluorine.
To start out, download and install the latest version of the Fluorine Gateway. Zoli made some upates since my last posting that will allow your program to run on a shared hosting website. Worked for my last sample anyway. This also goes to show that the folks at The Silent Group are extremely helpful when issues arise with Fluorine. I don’t think the guy sleeps.
Setting up the project folder structure
Start out by setting up a project folder structure by creating a master project folder with a Dot Net and a Flex folder in it. Mine is D:\Projects\Flex\Fluorine Flex 2.
Setting up the Visual Studio project
This time we’ll use the new Fluorine ASP.NET Web Application template. Fire up Visual Studio and create a new web site project. Select the Fluorine template and set up a virtual web directory that points to the project folder’s Dot Net folder. For me it’s http://localhost/fluorineflex2/. If you tried the last tutorial, you’ll quickly figure out that this template saves you a LOT of steps. Once you’ve created the project, you only have to do one thing to configure Fluorine.
Configuring the Fluorine Gateway in Visual Studio
Open the WEB-INF/flex/services-config.xml file and modify the endpoint node to point to the virtual web directory location of Gateway.aspx. In my case it was http://localhost/fluorineflex2/Gateway.aspx. You should be able to copy and paste this url into a browser and a blank page should load without any error messages.
UPDATE
I was not aware of this, but I recieved an email from Zoli at The Silent Group and he notified me that the uri attribute does not have to change as long as the context root remains the same on the server you are deploying to. In this case if I’m developing on http://localhost/fluorineflex2/Gateway.aspx, and deploying to http://www.darbymedia.com/fluorineflex2/Gateway.aspx, then I can leave the uri attribute as it’s default: http://{server.name}:{server.port}/{context.root}/Gateway.aspx. You should still however be able to copy and past the actual URL of the Gateway.aspx file and the blank page should load without any errors. It’s a quick way to check for issues if you are not getting the results you want.
Setting up the Flex project
Start Flex Builder 2 and create a new Flash Data Service Project with the Root folder pointing to the Dot Net folder of your project directory (D:\Projects\Flex\Fluorine Flex 2\Dot Net) and the Root URL pointing to the virtual web directory for the Visual Studio project (http://localhost/fluorineflex2/). The Context root should be the final bit of the virtual web path (/fluorineflex2). Enter the project name (fluorineflex2) and use the Flex directory of your project folder as the location of your Flex project files (D:\Projects\Flex\Fluorine Flex 2).
If you need help with any of the stuff above please check out the previous tutorial.
Creating a C# compact disc value object class
To start out we’ll need a value object on the server side to pass back to Flex when requested. Let’s create a class called CompactDiscVO that has the properties, artist, title, and year.
[csharp]
using System;
namespace com.darbymedia.medialibrary.vo
{
public class CompactDiscVO
{
public string artist;
public string title;
public string year;
public CompactDiscVO(){}
}
}
[/csharp]
Creating a Fluorine Gateway service class
To start out, create a basic service that you can point to with your Flex RemoteObject. We’ll also include a method called getCDVO that will instantiate and return a CompactDiscVO object.
[csharp]
using System;
using System.Web;
using com.darbymedia.medialibrary.vo;
namespace com.darbymedia.medialibrary.service
{
public class MediaLibraryService
{
public MediaLibraryService() { }
public CompactDiscVO getCDVO()
{
CompactDiscVO cd = new CompactDiscVO();
cd.artist = “Redd Kross”;
cd.title = “Third Eye”;
cd.year = “1990″;
return cd;
}
}
}
[/csharp]
Now we can move on to Flex Builder.
Creating a corresponding Actionscript compact disc value object
In Flex Builder 2 create a CompactDiscVO class. I’ve added a toString() method for demonstration purposes (good for debugging too!). Note that the namespace/package path and class name are the same. This is not absolutely necessary but makes sense here.
[actionscript]
package com.darbymedia.medialibrary.vo
{
public class CompactDiscVO extends Object
{
public var artist:String;
public var title:String;
public var year:String;
public function CompactDiscVO(){}
public function toString():String
{
var s:String = “[CompactDiscVO]“
s += “\nArtist: ” + artist;
s += “\nTitle: ” + title;
s += “\nYear: ” + year;
return s;
}
}
}
[/actionscript]
Building the main MXML application file
This time I’ve decided to use Actionscript only for my RemoteObject file. This way you can compare with the previous tutorial which used an MXML RemoteObject if you want. On creationComplete, the initApp() method instantiates the remote object with the destination, which is the destination name used in the services-config.xml file, the source, which is the namespace and class path of the C# service class we created, and listeners for result and fault events. Here is the MXML:
[xml]
[/xml]
At this point we’re calling the getCDVO method on the service immediately. roResult will trace out information to the console when run in debug mode. Go ahead and do that and you’ll get the following:
[SWF] /fluorineflex2/fluorineflex2/FluorineFlex2-debug.swf – 628,788 bytes after decompression
[object Object]
Redd Kross
null
Interesting. In roResult, our first trace of event.result gives us [object Object] so obviously we’re getting an Object type, but our CompactDiscVO.toString() method is not kicking in. Our second trace of the event.result.artist property, Redd Kross, shows that our data came through but when we try to put it into a typed variable as CompactDiscVO for our third trace we get null. So we are getting data back but it’s not typed correctly. It’s just a generic Object with properties.
To force the correct mapping you have use the flash.net.registerClassAlias() method. You pass it the full namespace and class name of the server side class as a string and the Actionscript class you want it to map to, like so:
flash.net.registerClassAlias(”com.darbymedia.medialibrary.vo.CompactDiscVO”, CompactDiscVO);
I put this in the initApp() method.
Now our MXML looks like this:
[xml]
[/xml]
Run this in debug mode and you can see that our first trace of event.result gives us the toString() output of the CompactDiscVO class, our second trace of event.result.artist gives us Redd Kross, and our third trace of the typed variable, cd, prints out not null, but the toString() output as well! Pretty cool!
Doing something with the content
Now let’s put this data into display items. I’ve added some labels and input fields to hold the data, a button to trigger the return, and an alert for any faults. I’ve put the call to the remote object’s getCDVO in the button’s click handler this time. Check it out:
[xml]
[/xml]
Check it out here.
Sending is easy…next time we’ll do that.
How to Set Up .NET Remoting with Flex 2 and Fluorine
UPDATE: Please note that this post is from January of 2007. While it may help you in some respects, I have not been working with Fluorine so I will not be able to answer any questions. Also, any SWF embeds or links to working code are no longer valid. I have moved from a personally managed WordPress install to WordPress hosting so the formatting has been affected as well. MXML is not displaying at all and I’ll try to fix this in the near future.
-
- Create a project folder. I called mine Fluorine Flex.
- Inside this folder create a folder called
Dot Netfor the .NET code and a folder calledFlexfor the Flex code
- Start Visual Studio. I’m using Visual Studio 2005 Standard Edition
- Select
File -> New -> Web Site - In the dialog box, select
Empty Website - In the language dropdown select
C# - In the location dropdown section select
http - On the right select
Browse - In the new dialog on the left select
Local IIS - On the top right select
Create new virtual directory - Enter an alias name. I entered
fluorineflex. - Browse and select the
Dot Netfolder in your project folder and selectOK - Now select the virtual directory you just created and select
OK
- In the Solution Explorer, right-click the project icon and select
Add Reference - Under the .NET tab locate and select
Fluorineand selectOK - Right-click the project icon and select
Add New Item - Select
Web Configuration File. Leave the name as is and selectAdd - In the
system.webnode of the newWeb.configfile add the following:[xml][/xml]
and save. - Right-click the project icon and select
Add Item - Select
Web Formand name the fileGateway.aspx
- Right-click the project icon and select
New Folder. Name itWEB-INF. - Right-click the new
WEB-INFfolder and selectNew Folder. Name itflex. - Right-click the new
flexfolder and selectAdd New Item. - Select
XML Fileand name itservices-config.xml. - Open the new
services-config.xmland paste the following content below the initialxmltag:
[xml]*
[/xml]
In theendpointnode change theuriproperty to point to Gateway.aspx in the virtual directory that you created. In my case it’suri="http://localhost/fluorineflex/Gateway.aspx".
Download and install(Not necessary – Thanks Richard!)Flex Data Services 2 Expressfrom the Flex download page. No serial number is neccessary for development work.- Start Flex Builder 2.
- Select
File -> New -> Flex Project. - Select
Flex Data Services. - Select
Compile application locally in Flex Builder. - Select
Next. - Uncheck
Use default local Flex Data Services Location. - In
Root folderselectBrowseand select theDot Netfolder in your project folder. For me it wasD:\Projects\Flex\Fluorine Flex\Dot Net. - In
Root URLenter the path to the virtual directory created earlier. In my case it washttp://localhost/fluorineflex/. - As far as I can tell
Context rootshould be the last folder name of your virtual web directory. Mine was/fluorineflex. - Select
Next - Uncheck the
Use default locationbox and select theFlexfolder in you project folder. In my case it’sD:\Projects\Flex\Fluorine Flex\Flex. - Select
Finish
- In Visual Studio, right-click the project icon and select
Add New Item. - Select
Classand name itFlexService.cs. (You can name the class anything you want at this point. I’ll use FlexService.) - When prompted to put the file in an
App_Codefolder, clickYes. - At this point you’ll only need to “use”
SystemandSystem.Web, but it won’t hurt to leave the other classes. - I’ve cleaned up mine and given it a fancy namespace (com.darbymedia.com.flex) to look like this:
[csharp]
using System;
using System.Web;namespace com.darbymedia.flex
{
public class FlexService
{
public FlexService() { }
}
}
[/csharp] - Add a function called
echoand have it return a message that is sent and our class looks like this:
[csharp]
using System;
using System.Web;namespace com.darbymedia.flex
{
public class FlexService
{
public FlexService() { }public string echo(string msg)
{
return “echo(msg) msg: ” + msg;
}
}
}
[/csharp]
- Return to Flex Builder 2
- Create a RemoteObject with the following properties:
- destination:
fluorine. This is the destination set up in theservices-config.xmlfile. - source:
com.darbymedia.flex.FlexService. This is the namespace and class name for our service. - result:
roResult. This is method to receive the return value. We’ll do this in a minute. - fault:
roFault. This is the method to receive any errors. We’ll make this in sec too.
Adding an input field, submit button, output text area and a bad call button your mxml will look like this:
[xml][/xml]
- destination:
- Now to create the roResult and roFault methods as well as button handlers. They look like this:
[xml][/xml]
- Now that we have our RemoteObject set up we can call any method within our service class directly.
I’ve finally gotten the bug to get this Flex thing going and my only stumbling block for really useful applications was that for server-side stuff I’m way more adept with Visual Studio .NET and C# than I am with PHP. I had been looking for a solution and found WebOrb, which requires a server install and isn’t free for AMF3. Since I use shared hosting this is really not an option. Then I found Fluorine created by Zoltan Csibi at The Silent Group which is open source after a bit of teeth gnashing easy to set up. After finding Sam Shrefler’s excellent examples I decided to put something together something that was a bit more basic than his Cairngorm examples (Not that those weren’t basic and very helpful!).
I’m going to start out with something extremely simple just to demonstrate the setup. The service will basically echo back any string you send to it. I saw this morning as I got the latest update that it installs a Visual Studio project template that is probably the best way to go when setting things up, but I’ve decided to go ahead and go through the steps of setting things up manually so that hopefully the purpose of the elements is more clear.
Download and Install the Fluorine Gateway
Go to the Fluorine download page and download and install the Fluorine Windows Installer.
Setting Up Project Folders
Creating the Project in Visual Studio .NET
Configuring the Project in Visual Studio .NET
I gleaned this mostly from Setting up a Flash Remoting-enabled ASP.NET application at the Fluorine site.
The next part is from the Flex2 basic setup section within Using Flex2 and AMF3 on the Fluorine site.
UPDATE
I was not aware of this, but I recieved an email from Zoli at The Silent Group and he notified me that the uri attribute does not have to change as long as the context root remains the same on the server you are deploying to. In this case if I’m developing on http://localhost/fluorineflex/Gateway.aspx, and deploying to http://www.darbymedia.com/fluorineflex/Gateway.aspx, then I can leave the uri attribute as it’s default: http://{server.name}:{server.port}/{context.root}/Gateway.aspx.
Configuring the Flex Project
Now we’re ready to ROCK!
Creating a Fluorine Service in .NET
Accessing the Service via Flourine in Flex
Now the entire mxml file looks like this:
[xml]
[/xml]
Give it a whirl! Type a message and submit it. Then click the bad call button to see what happens when an error occurs. Here’s what it’s supposed to look like.
This is super basic but it should get you up and running. Next I’ll be sending and receiving objects, but something tells me you’ll figure that out on your own!
The Elusive drawFocus Property
Yesterday I was dealing with problem where a TextField with no border on a white background was showing a faint green or blue border, not when clicked, but when tabbed to.
I searched and searched for the issue and came up empty, but of course my colleague Mathew “RayRay” Ray found a solution in seconds.
Best as I can tell, between Halo styling and FocusManager this problem is occurring. It can be solved by using:
_myTextField.drawFocus = false
Once I had drawFocus, a search on it led to _myTextField.drawFocus = “” as well.
I can’t find any official documentation on drawFocus anywhere and searches like: actionscript focusmanager blue rectangle were not turning anything up. Hopefully once this page is indexed it will help someone!
What Was FITC?
Well I just got back from my very first Flash conference, FITC, and it was a pretty good milestone for me.
It was great to finally meet my online friend Stacey who has given me tons of advice over the last year or two. I also met many new people, Kristin, Lord Alex, Eric, Steve, Paolo, Tony, Julian, James, Randy, Michael, Lanny. I’m sure I’ve left some out. I’ve got some pictures here, but Stacey’s are much more entertaining. Of course it was awesome having my fellow work peeps Mathew Ray and Adam Boozer there too!
Aside from the fun, I got some pretty serious inspiration and motivation to further my knowledge of what is now known as the Flash Platform. There were several sessions that were busts, one in particular in my opinion was the keynote. I don’t think that anything groundbreaking was announced and I thought that much of it was just filler. Can you say “exciting?”
The most inspiring session was Joshua Davis’ Mutation themed talk. I was strangely freaked when Adam dropped the F-Bomb but Joshua put everyone to shame in that department. The guy just does amazing work in the punk aesthetic (inspirational to me) which is also creative, and technical. Other sessions that I especially enjoyed were by Colin Moock, Darron Schall, Mario Klingemann, Keith Peters, Adam Boozer, the Next Generation panel, and the Freelancing panel. Some others were interesting and others I found fairly bland.
On Sunday the combination of Colin Moock’s and Darron Schall’s talks made me realize the potential of what Adobe is trying to do with the Flash Platform and Apollo. Not that I’m well read in the Flash/Flex blogosphere, but I haven’t seen anyone state this yet: Adobe is aiming directly at the .NET market. With the free dev framework, the lightweight runtime platform (much smaller than MS .NET), the cross-platform (I hope) compatibility, and the promise of desktop apps with Apollo, I’m definitely buying in. From what I can see from the sessions by Collin, Darron, and Keith, the entirely rewritten version 9 player will open up a whole new range of possibilities, AND respect for the Flash Platform.
Joyce came up and we stayed a couple of extra days after the conference to enjoy Toronto. The extra time brought up feelings I’ve had about Canada, Toronto and Hockey which will be upcoming posts.
Comments(9)
Leave a Comment
Comments(6)


