Friday, July 1, 2011

Hello World Facebook App (Python Version)

Introduction

Note: This application will only work if the user accessing the app is logged in and has already installed the facebook app. This would also work if the user accessing the app is the creator/developer of the app. I would make a follow-up post to make this app more general.

I started reading on Facebook Apps days ago. I was quite interested to discover the capabilities of this framework. It might come in handy later on.

So after reading the documentation and browsing the example Apps, I was not quite confident that I understood things. I remembered that every time I tried to learn a programming language, all the books/tutorials that I have read always started with a simple "Hello World" program. This program sort of like breaks the ice and gives you a feeling of accomplishment afterwards. There is something about you actually doing the coding and seeing the result of your code that makes you feel more confident, happy, even ecstatic afterwards.

In this blog post, I will discuss how I made a simple "Hello World" program for Facebook Apps. I plan to extend this in succeeding posts, add more functionality. But for now, I'll try to keep it as simple as possible so as to ease my entry into this new framework. I'm sharing this experience as this might also be useful to others trying to learn Facebook Apps.

Facebook Canvas

A Facebook App is just like any other web application. The only difference is that it is run inside an iframe in Facebook. Because of this, you could use almost any web application framework. You have the liberty to choose the programming language, database, web server, etc. The iframe in which the Facebook App is loaded is referred to as the Facebook Canvas.

When your web application/Facebook App is loaded into the iframe, the web application is requested using the POST method rather than the GET method. It is requested using the POST method as Facebook passes some POST parameters that identifies the Facebook user loading the application. This becomes clear in our "Hello World" application.

Create the Facebook App

To create the Facebook App, click on the "Create New App" button at the top right-hand corner of this page, https://developers.facebook.com/apps. Just fill in the requested information and you will have your new application. The most important thing to take note of is the canvas url as shown below.


In this case, I set the "Canvas URL" to a local address of a machine that runs the web server. Actually, this is an address of an Ubuntu Server that runs in Virtualbox. If you are running the web server locally in your computer, you can use "http://localhost:8888/" instead.

Tornado Webserver

Because of its simplicity and my preference, I would be using the Tornado Webserver. Its setup is quite easy just follow the documentation and you'll have the server ready in no time. You can easily install it using:

$ easy_install tornado

Initial Program

After installing Tornado, create the main.py script with the following code.


import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def post(self):
        self.write("Hello, world")


application = tornado.web.Application([
    (r"/", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

To understand the code better, it would be nice if you could go through the first few sections of the Tornado documentation. 

The "MainHandler" class above handles requests made to the root, '/', of the webserver. The "post" method of the class responds to POST requests. Notice that we don't have a "get" method as the application will be requested using the POST method as mentioned earlier.

Running this webserver, using the command below

$ python main.py

would allow as to access the Facebook app. The app is accessible through the url below.

http://apps.facebook.com/[appname]/

Replace the [appname], with the app name you assigned to your app. This would produce the following output.

Better Hello World

There we have a basic "Hello World" app. But honestly, it does not feel that it exposes even a itsy-bitsy Facebook functionality. So lets improve it.


import tornado.ioloop
import tornado.web


import base64
import json
import hmac
import hashlib
import time
import urllib2


def base64_url_decode(data):
    data = data.encode(u'ascii')
    data += '=' * (4 - (len(data) % 4))
    return base64.urlsafe_b64decode(data)


def load_signed_request(signed_request, app_secret):
    try:
        sig, payload = signed_request.split(u'.', 1)
        sig = base64_url_decode(sig)
        data = json.loads(base64_url_decode(payload))


        expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest()


        if sig == expected_sig and data[u'issued_at'] > (time.time() - 86400):
            return data
        else:
            return None
    except ValueError, ex:
        return None


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


    def post(self):
        app_secret = '############' # put your app secret here
        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, app_secret)


        user_id = data.get(u"user_id")
        url = 'https://graph.facebook.com/%s'%user_id
        fd = urllib2.urlopen(url)
        response = fd.read()
        fd.close()
        userdata = json.loads(response)
        self.write('Hello <a href="%s">%s</a>\n'%(userdata['link'], userdata['name']))




application = tornado.web.Application([
    (r"/", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

The code above seems to be a very big change from our original version. But actually its not that complex.

The improved version uses the POST variables passed by Facebook to our application to enable it to display a more personalized output. For example, Juan dela Cruz, a Facebook user, runs the application, it would display:

Hello Juan dela Cruz

This is possible because when Facebook displays the application, it makes a POST request to the Canvas URL. With the request are POST variables, the important variable is 'signed_request'.


def post(self):
        app_secret = '############' # put your app secret here
        signed_request = self.get_argument('signed_request')
        data = load_signed_request(signed_request, app_secret)


'signed_request' contains signed information about the Facebook user accessing the application. To make it usable, the application decodes its contents using the 'load_signed_request' function. This function is simply a transliteration of the PHP code in their documentation. You could take time to understand it, but for now its enough that it simply decodes the information passed to the application. The contents of 'signed_request' decodes to a JSON string which is returned by 'load_signed_request' as a dictionary object.

{u'user_id': u'11111111', u'algorithm': u'HMAC-SHA256', u'expires': 1309536000, u'oauth_token': u'############', u'user': {u'locale': u'en_US', u'country': u'ph', u'age': {u'min': 21}}, u'issued_at': 1309531299}

From this dictionary object, we use the 'user_id' to get more information about the user using the Facebook Graph API.

        user_id = data.get(u"user_id")
        url = 'https://graph.facebook.com/%s'%user_id
        fd = urllib2.urlopen(url)
        response = fd.read()
        fd.close()
        userdata = json.loads(response)
        self.write('Hello <a href="%s">%s</a>\n'%(userdata['link'], userdata['name']))

We get more information about the user by making a GET request to this URL.

https://graph.facebook.com/[user_id]

The response to this request is a JSON string containing more information about the user. An example of the returned JSON string follows.

{"id":"11111111","name":"Juan dela Cruz","first_name":"Juan","last_name":"dela Cruz","link":"http:\/\/www.facebook.com\/people\/Juandelacruz\/111111111","gender":"male","locale":"en_US"}

From this JSON string, we get the name of the user and other information to make a personalized output.

Ending

The application we discussed is a very simple application. It even does not tackle user authentication, which will allow the application access to more information. However, I believe that it is fitting as a "Hello World" app to give an idea on how a Facebook app works. I'll improve on this app in a later post.

1 comment:

  1. I have read this blog, this information in this blog is really informative and inspirational as well as it helped me a lot in making my assignment. Thanks
    iPhone App Developers

    ReplyDelete