Naming Prism(CAL) Modules and Classes in a Silverlight Project
I recently just put together my second WPF based project using Prism (Composite Application Guidance). In this case it was a Silverlight project.
I ran into a problem after a few weeks where I was getting strange library path errors in my c.gs files. My structure was to have a solution containing a separate project for each Module. So I would have MySolution and it would have ModuleA.csproj, and ModuleB.csproj as projects. Those projects would have main classes called ModuleA.cs and ModuleB.cs. Here is what I learned: DO NOT name your project’s main class with the same name as your project. In fact, in my case even a new name that consisted of part of the project name did not work. I had a project called SearchModule.csproj. The main class was SearchModule.cs. Renaming it to Search.cs still threw errors. In the end, even though it’s not the best name in the world (time is a factor here) SearchModuleClass.cs solved the problem.
Hopefully if you’ve found this post it will save you some time.
R.I.P. Lux Interior

UPDATE (2/14/09): There’s an update on The Cramps site that Lux died of an Aortic Dissection. It is suggested that in lieu of flowers, donations be made to Best Friends Animal Shelter.
It’s a very sad day. Yesterday Lux Interior died.
Statement from their publicist Girlie Action’s website:
“Lux Interior, lead singer of The Cramps, passed away February 4, 2009 due to an existing heart condition at Glendale Memorial Hospital in Glendale, California at 4:30 AM PST at the age of 62. Lux has been an inspiration and influence to millions of artists and fans around the world. He and wife Poison Ivy’s contributions with The Cramps have had an immeasurable impact on modern music.
The Cramps emerged from the original New York punk scene of CBGB and Max’s Kansas City, with a singular sound and iconography. Their distinct take on rockabilly and surf along with their midnight movie imagery reminded us all just how exciting, dangerous, vital and sexy rock and roll should be and has spawned entire subcultures. Lux was a fearless frontman who transformed every stage he stepped on into a place of passion, abandon, and true freedom. He is a rare icon who will be missed dearly.
The family requests that you respect their privacy during this difficult time.”
The Daily Swarm – lots of quotes and video clips.
Thanks Philip Lynott
I heard Gary Moore sing this tonight:
‘I’ve been spending my money in the old town
It’s not the same Honey, when you’re not around
I’ve been spending my time in the old town
I sure miss you Honey, now you’re not around
now you’re not around this old town’
And of course the original song finishes with :
‘This boy is crackin’ up
This boy has broke down…’
Words I’ve taken to heart in the recent past in a big way.
It also applies to my thoughts on Phil – I really miss you.
I’m so happy that you’re remembered.
I’ve been on a Thin Lizzy tear of late. Several DVDs have come out recently. Overview to come!
Bless you Phil, you still inspire! You and your music help to keep me, at least, together.
On My Brilliant Nokian Winter Tires
Having lived in Atlanta for 22 years I always wondered why anyone would need an all wheel or four wheel drive sedan. Then I moved to Denver and it seemed that the Subaru was the state car. I was commuting at the time and when the first snow arrived the inch or two turned to slush and my drive to work on the local interstate was alarming at best. After all I had all weather tires. The next snow came innocently enough and though the surface streets were fine, I stopped at the local Starbuck’s drive-thru and after making my purchase I found myself sliding down the hill on a sheet of ice right into traffic. Knowing that I was going to be going to Winter Park regularly during the winter, I sought out some snow tires.
I drive a 1995 Mercedes E320. It’s very heavy and a rear wheel drive. I knew that I needed something to keep myself safe. This was driven home in September 08 when I tried to return from my Girlfriend’s house in Winter Park to Denver during a rain storm. I was watching the outside temperature reading in my car as I headed up Berthoud Pass. Once it hit 32 degrees the snow started sticking. The tires started slipping and about two miles from the top of the Continental Divide I was losing traction fast. Before the spinning of my wheels got too bad I turned around. I didn’t want to get stuck or in a ditch. I went back down and borrowed Kristy’s beater Cherokee for the trip back. I saw a few people that weren’t prepared in the ditch on the way back. Kristy drove my car to Denver later in the week when she came to visit me in Denver.
My search for winter tires led me to Nokian, a Finnish company. Their studded Hakkapeliitta 4 tires came highly recommended through my internet research. I bought them at Meadow Creek Tire in Denver. When they were done applying the studs and mounting the tires the sales guy said, “You’ve got a snowmobile now.” Was he ever right.
Since they’re studded they’re noisy as hell, but driving on ice and snow with these tires really is not much different than on pavement. I can safely out perform any front wheel drive and almost any four wheel drive. They’ve gotten me out of many sticky situations and now I actually look forward to less than perfect road conditions when I drive Berthoud Pass.
For safety and downright fun in winter driving I highly recommend these tires. I’m so impressed that since I need a new set of summer tires I might actually consider Nokian for that set as well.
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>
The Netflix API Part 1 – Making Your Initial Request
Please note that all keys, tokens and signatures in this series are made up and not usable.
I was excited when I found that the Netflix API was released. I also found that a lot of what was out there less than clear. What’s new? So I thought I’d share and explain what I find as I take my steps to understand it. I plan to start with Actionscript and Flex and then to move on to C# with WPF with the goal of taking it to the Microsoft Surface that we have at EffectiveUI.
To start with, go ahead and go to the developers section at Netflix and sign up for a developer account. You don’t need to have a Netflix account to get started, you can create up to three test accounts once you sign up, but you won’t be able to use some features that a full fledged subscriber would be able to. You should be fine to start out with though. FYI I’ll be using the Netflix REST API.
Netflix uses a newish standard for account access called OAuth. It allows a subscriber of a site such as Netflix to allow third parties sites and applications to access to their account assets without having to give their Netflix username and password to that site or app. A common example is if I have a Flickr account and I want make prints with a third party photo printer, the photo printer rerouts me to Flickr, where I log in and tell Flickr that I want to allow the printer to have read only access to my photos. Flickr then passes back what’s called a token that the third party can use to gain access to my Flickr photos so that I can select and print them.
Yes this can get a little convoluted, but before I get started with code, I’m going to send you to Hueniverse where there is an excellent four part overview of how OAuth works (http://www.hueniverse.com/hueniverse/2007/10/beginners-guide.html). It is vital that you do this because I’m going to assume you’ve read it as I continue. It’ll only take about 10 minutes to read over it.
Since I’m sure you didn’t just that guide I’m going to define three terms:
- Consumer – is the application that is requesting access to protected data at the Service Provider on behalf of the User.
- User – is the person using the Consumer to access their data on the Service Provider.
- Service Provider – is the application that the Consumer is requesting access to on behalf of the User.
These are much more clearly defined in the overview at Hueniverse mentioned above. You should really read it.
A Basic Request
A basic OAuth request consists of the following items:
- oauth_consumer_key – the Netflix consumer key you received when signing up as a developer
- oauth_nonce – a unique item that can only be used once
- oauth_signature_method – In the case of Netflix it is HMAC-SHA1
- oauth_timestamp – time in milliseconds since 1/1/70
- oauth_signature – The key and message (described below) encrypted using the oauth_signature_method
Signatures
For OAuth to work all requests by the consumer to the service provider need to be accompanied by a signature. For Netflix this is a HMAC-SHA1 encoded key/message string that is tacked onto the end of the request. HUH? If you check out the pseudocode on the wikipedia link above, you’ll see that you’re gonna spend your entire week trying to figure out how to code that one. Luckily for us there is an AS3 OAuth library from iotashan.com at Google Code that handles all that stuff for us. It’s easiest just to build a swc file out of the code and include it in your project.
Okay back to the HMAC-SHA1 encoded signature. There is a key and a message.
The key is your Netflix consumer secret key, sometimes referred to as a shared secret concatenated with an ampersand ‘&’ with a secret token provided by the service provider. If you don’t have a secret token yet then you just let your key end with the ampersand character. A key without a secret token might look like this:
Kzb6wjrhgR&
The message is the url and query string using the properties and values listed above that makes up your request WITHOUT the oauth_signature. As a URL with query string it might look like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=420E8742-E341-6F5C-8E60-4973D0A85CAF&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225298006
You pass the key and message as parameters to the encryption method and get back your signature which might look like this:
cg5jE6EIFXU4th63HD3HFSHY4LL
It will become the value of the oauth_signature value.
In the end a url and query string to make an initial request for a token might look like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=420E8742-E341-6F5C-8E60-4973D0A85CAF&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225298006&oauth_signature=cg5jE6EIFXU4th63HD3HFSHY4LL
On the service provider side, they will take the exact same steps on the url and name value pairs to create a signature that should be the same as yours. Plus the nonce value and the timestamp are combined to create a unique value that can never be used again. This guarantees that the request can only be used once and not intercepted in anyway and used again.
Thankfully the AS3 OAuth library will take care of much of this for us.
Making an Initial Netflix Token Request using AS3 OAuth
I’ll be using Flex to create this example I’ll use a class and utilize HTTPService (Flex Framework) to show you how this can be done. This can also be done in straight AS3 with URLLoader.
First create a new project in Flex. I’m calling mine NetFlix Tutorial and under Application type select “Desktop Application (Runs in Adobe AIR).” Select the Next button twice and select the Library path tab. Select “Add SWC” or “Add SWC Folder” and select the location of the AS3 OAuth swc that you built. Select Finish.
In the src folder create a new folder called netflix. In netflix create a folder called tutorial. Now create an actionscript class called tokenRequest.
At the top of your class declaration import the oauth classes:
import org.iotashan.oauth.*;
Create several static constants to hold some unchanging stuff you’ll need:
private static const CONSUMER_KEY:String = "INSERT YOUR CONSUMER KEY HERE"; private static const:CONSUMER_SECRET:String = "INSERT YOUR CONSUMER SECRET HERE" private static NETFLIX_BASE_URL:String = "http://api.netflix.com/"; private static const SIG_METHOD:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
Before people start howling, yes, storing your secret key in an easily uncompilable swf is dangerous. I would advise you to store your secret key externally and read it in when the app starts.
Now create a consumer instance variable:
private var _consumer:OAuthConsumer;
The constructor which will instantiate our _consumer variable by passing the consumer key and consumer secret to it:
public function TokenRequest()
{
_consumer = new OAuthConsumer(TokenRequest.CONSUMER_KEY, TokenRequest.CONSUMER_SECRET);
}
Just for testing lets create a function that will return the request url and query string:
public function getRequestURL():String
{
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
return tokenRequest.buildRequest(TokenRequest.SIG_METHOD);
}
The reqUrl is build by using th base url plus the path to the request_token method. The reqest object is created by passing the method, in our case “GET”, the request url, any additional properties – there are none since this is our initial token request, and the consumer object. The call to build request generates our full request with signature and returns it.
Now back in our Main.mxml let’s add some tags:
- Script
- TextArea – id=”txtDebug”, width=”100%”, height=”100%”
- Button – id=”btnRequestToken”, label=”Request Token”, click=”btnRequestToken_Click(event)”
In the application tag set layout=”vertical”.
In the script tag, import the Token Request tag, create the btnRequestToken_Click and requestToken methods.
import netflix.tutorial.TokenRequest;
private function btnRequestToken_Click(event:MouseEvent):void
{
requestToken();
}
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
txtDebug.text += tokenReq.getRequestUrl() + "\n";
}
Your output should look something like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=8ED39931-8CD6-9ADE-D8BE-49E177B2FD12&oauth_signature=cg5jE6EIFXU4th63HD3HFSHY4LL%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225305192
Now lets wire up our class to make the actual request to Netflix.
Back in the TokenRequest class, lets start with the Flex-centric HTTPService class.
Import the http service class:
import mx.rpc.http:HTTPService;
Below the _consumer instance variable declaration add:
private var _service:HTTPService;
In the constructor add:
_service = new HTTPService(); _service.addEventListener(ResultEvent.RESULT, onServiceResult); _service.addEventListener(FaultEvent.FAULT, onServiceFault);
Above the class declaration add event metadata so we can relay the events:
[Event(name="result", type="mx.rpc.events.ResultEvent")] [Event(name="fault", type="mx.rpc.events.FaultEvent")]
Add the onServiceResult and onServiceFault methods:
private function onServiceResult(e:ResultEvent):void
{
dispatchEvent(e);
}
private function onServiceFault(e:FaultEvent):void
{
dispatchEvent(e);
}
Add a requestToken method to initiate the request:
public function requestToken():void
{
_service.url = getRequestUrl();
_service.send();
}
Now in the main.mxml file add listeners and methods to the tokenReq object and add the call to tokenReq.requestToken():
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
//txtDebug.text += tokenReq.getRequestUrl() + "\n";
tokenReq.addEventListener(ResultEvent.RESULT, onRequestTokenResult);
tokenReq.addEventListener(FaultEvent.FAULT, onRequestTokenFault);
tokenReq.requestToken();
}
private function onRequestTokenResult(e:ResultEvent):void
{
txtDebug.text = e.result.toString();
}
private function onRequestTokenFault(e:FaultEvent):void
{
txtDebug.text = e.fault.message;
}
Run the app and your output should look similar to this:
oauth_token=wapq45jpvnaldkuezcdm4qj7&oauth_token_secret=JbBM7avBarg5&application_name=My+Cool+Netflix+Viewer&login_url=https%3A%2F%2Fapi-user.netflix.com%2Foauth%2Flogin%3Foauth_token%3Hwapq43jpvnaghjueycdm4nk5
This result has all the stuff you need to get going with the Netflix API. What can you do with this? Stay tuned.
Here is all the source:
Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import netflix.tutorial.TokenRequest;
private function btnRequestToken_Click(event:MouseEvent):void
{
requestToken();
}
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
//txtDebug.text += tokenReq.getRequestUrl() + "\n";
tokenReq.addEventListener(ResultEvent.RESULT, onRequestTokenResult);
tokenReq.addEventListener(FaultEvent.FAULT, onRequestTokenFault);
tokenReq.requestToken();
}
private function onRequestTokenResult(e:ResultEvent):void
{
txtDebug.text = e.result.toString();
}
private function onRequestTokenFault(e:FaultEvent):void
{
txtDebug.text = e.fault.message;
}
]]>
</mx:Script>
<mx:TextArea id="txtDebug" width="100%" height="100%" />
<mx:Button id="btnRequestToken" label="Request Token" click="btnRequestToken_Click(event)" />
</mx:WindowedApplication>
netflix.tutorial.TokenRequest
package netflix.tutorial
{
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import org.iotashan.oauth.*;
[Event(name="result", type="mx.rpc.events.ResultEvent")]
[Event(name="fault", type="mx.rpc.events.FaultEvent")]
public class TokenRequest extends UIComponent
{
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();
private var _consumer:OAuthConsumer;
private var _service:HTTPService
public function TokenRequest()
{
_consumer = new OAuthConsumer(TokenRequest.CONSUMER_KEY, TokenRequest.CONSUMER_SECRET);
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, onServiceResult);
_service.addEventListener(FaultEvent.FAULT, onServiceFault);
}
private function onServiceResult(e:ResultEvent):void
{
dispatchEvent(e);
}
private function onServiceFault(e:FaultEvent):void
{
dispatchEvent(e);
}
public function requestToken():void
{
_service.url = getRequestUrl();
_service.send();
}
public function getRequestUrl():String
{
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
return tokenRequest.buildRequest(TokenRequest.SIG_METHOD);
}
}
}
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!
I Work at EffectiveUI!
Well after 15 months of contracting, mostly on one particular staff augmentation gig, I’ve wrangled my way into EffectiveUI as a “Senior Software Engineer.” Not totally sure what that means but I do know that I am feeling happy and hopeful that I’ll be able to realize my full potential as an RIA developer. I can’t tell you how excited I am to finally be working amongst all of the incredibly talented and smart people here.
AND I live across the street from the office. How cool is that?!?
Changing Hosting Setup
I’ve been paying out the nose every month for hosting my various sites when really since it’s a blog I can host it for free! Which makes sense since I’d like to leave this information up but I really don’t post too often. Redirecting my domain darbymedia.com to WordPress and utilizing Google Apps for email. This also means that for now images may not display until I get everything converted over. The Fluorine stuff I posted way back when will not be available for now either.
eMusic Picks for November 07
I haven’t been keeping up with this but I’ll try from now on!
Clorox Girls – Jaime Les Filles (thanks to Plan Nine Rock Show)
Beth Hirsch – Titles & Idols (thanks to Dave’s Lounge)
Beth Hirsch – Life Is Mine
Beth Hirsch – P-Town Rubies
Beirut – The Flying Club Cub (thanks to Criminal Records)
Of Montreal – Hissing Fauna, Are You The Destroyer?
Thom Yorke – The Eraser
The Octopus Project – Hello Avalanche
Shriekback – Care, Tench And Related Tracks
Lene Lovich – Shadows and Dust (First Four Tracks)
Leave a Comment
Leave a Comment
Leave a Comment


