In this post I hope to get you off the ground in terms of how to request data
from other sites using the OpenSocial API. Remember that in OpenSocial your
application is hosted in the browser and served off the container's domain (in
this case, MySpace). XHRs (XML Http Requests) cannot be issued across domains
(for example, Javascript hosted off of
http://www.myspace.com cannot access data from
http://www.google.com). Because of that
restriction, you can't make a straight call to another domain. The OpenSocial
API provides a simple mechanism called "makeRequest" that allows you to request
data from any domain you want.
MakeRequest gets around the XHR cross-domain restriction by utilizing a custom
http proxy hosted by MySpace. This proxy relays incoming requests to other sites
and pipes the results back to the browser. It also optionally signs the requests
using the OAuth methodology (the specifics will be covered in another article).
It is important to note that this is not a standard open proxy--it does its best
to verify that the request came from a valid OpenSocial application hosted on
MySpace.
MakeRequest method signature:
opensocial.makeRequest(url,callback,opt_param)
url
The url you wish to be called.
callback(data)
The function you wish to be called with the results. The signature of the
function should have at least one parameter (which will contain a reference to
the data returned by the call).
opt_param
A bucket for a set of standard OpenSocial parameters that tell the proxy how to
behave. There is quite a bit of functionality accessible through the
manipulation of this object. To give you a taste of what you can do, below is
some example code that exercises opt_param to its fullest.
code snippet 1 : Drill down into makeRequest parameters
function getRss(){
var url = "http://www.codeproject.com/webservices/articlerss.aspx?cat=1";
var params = {};
//Lets makeRequest know that the result will be an XML document.
params[opensocial.ContentRequestParameters.CONTENT_TYPE] =
opensocial.ContentRequestParameters.ContentType.XML;
//Sets the request method to POST (default is GET).
params[opensocial.ContentRequestParameters.METHOD] =
opensocial.ContentRequestParameters.MethodType.POST;
//Adds two custom headers to the request. These headers will be passed on to the target.
params[opensocial.ContentRequestParameters.HEADERS] =
{"x-myheader":"myheadervalue", "x-myotherheader":"myotherheadervalue"};
//Tells the proxy to sign the request using OAuth.
params[opensocial.ContentRequestParameters.AUTHORIZATION] = opensocial.ContentRequestParameters.AuthorizationType.SIGNED;
opensocial.makeRequest(url, makeRequest_callback, params);
}
So goes the request. opt_param takes a bucket of constants that you can use to tweak the request:
opensocial.ContentRequestParameters.CONTENT_TYPE
(takes a value from: opensocial.ContentRequestParameters.ContentType)
opensocial.ContentRequestParameters.METHOD
(takes a value from: opensocial.ContentRequestParameters.MethodType)
opensocial.ContentRequestParameters.HEADERS
(takes a key-value bucket)
opensocial.ContentRequestParameters.AUTHORIZATION
(takes a value from: opensocial.ContentRequestParameters.AuthorizationType)
This is not the official documentation for makeRequest, so I won't go into many details--instead I'll go on to create the app:). The code that parses the response is quite simple:
code snippet 2 : Response handling code
function makeRequest_callback(data,error){
if (data == null) {
$("rssDisplay").innerHTML = "no data returned--check your RSS url!";
return;
}
var channelList = data.getElementsByTagName("channel");
var channel = new RSSChannel(channelList[0]);
$("header").innerHTML = getTag("h1", channel.Title);
$("rssDisplay").innerHTML = channel.render();
}
The example application in this post is an RSS reader that uses a custom bit of
code that translates XML into JavaScript objects. Parts of that code
will be a lovely exercise in redundancy. Why? Because OpenSocial exposes an RSS
implementation that is more robust than the one in this article. This article is more about how to call a third party
site and parse the results (be they XML, JSON, or some other format). A lot of
sites happen to expose XML in RSS, so I'm using such exposure as an example. In
other words, this article continues the long and storied tradition of useless
demo-code--sit back and enjoy!
MakeRequest: Simple RSS Reader Application
MakeRequest is for pasing data to and receiving data from your site (or, in the
case of a mashup, a third party's site). Its method signature is:
First, let's set up some chrome and an initialization function:
code snippet 3 : Simple chrome for application
<style>
body {
background-color: whitesmoke;
}
h1 {
font-size: medium;
}
#rssDisplay {
border: 1px solid;
height: 240px;
overflow: auto;
padding: 3px;
background-color: white;
}
#rssDisplay p {
font-size: small;
}
</style>
<div id="container">
<div id="header">
</div>
<div id="rssDisplay">
</div>
<script>
function getRss(){
//this will get the feed
}
getRss(); //initialize the application
</script>
</div>
Above we have the trappings of an application--with no functionality. We'll now fill in the getRss() call with Javascript that calls the RSS feed (which happens to be the latest articles from "The Code Project").
code snippet 4 : Completed application
<style>
body {
background-color: whitesmoke;
}
h1 {
font-size: medium;
}
#rssDisplay {
border: 1px solid;
height: 240px;
overflow: auto;
padding: 3px;
background-color: white;
}
#rssDisplay p {
font-size: small;
}
</style>
<div id="container">
<div id="header">
</div>
<div id="rssDisplay">
</div>
<script>
/*
* makeRequest implementation
*/
function getRss(){
var url = "http://www.codeproject.com/webservices/articlerss.aspx?cat=1";
var params = {};
//This parameter lets makeRequest know that the result will be an XML document.
params[opensocial.ContentRequestParameters.CONTENT_TYPE] = opensocial.ContentRequestParameters.ContentType.XML;
opensocial.makeRequest(url, makeRequest_callback, params);
}
function makeRequest_callback(data, url, error){
if (data == null) {
$("rssDisplay").innerHTML = "no data returned--check your RSS url!";
return;
}
var channelList = data.getElementsByTagName("channel");
var channel = new RSSChannel(channelList[0]);
$("header").innerHTML = getTag("h1", channel.Title);
$("rssDisplay").innerHTML = channel.render();
}
/*
* Utility functions
*/
//Cross browser grabber for text in an xml element
function getNodeText(node){
if (node.text != null)
return node.text;
else
return node.textContent;
}
//Simple, non-performant tag renderer
function getTag(tag, txt){
return "<" + tag + ">" + txt + "</" + tag + ">";
}
//Adds a tag to a string
function appendTag(str, tag, txt){
str += getTag(tag, txt);
}
/*
* RSS Feed Wrapper Objects
*/
function RSSChannel(node){
for (var i = 0; i < node.childNodes.length; i++) {
var childNode = node.childNodes.item(i);
switch (childNode.nodeName) {
case "title":
this.Title = getNodeText(childNode);
break;
case "link":
this.Link = getNodeText(childNode);
break;
case "description":
this.Description = getNodeText(childNode);
break;
}
}
var items = node.getElementsByTagName("item");
this.RSSItems = [];
for (var i = 0; i < items.length; i++) {
var item = items.item(i);
var rssItem = new RSSItem(item);
this.RSSItems.push(rssItem);
}
}
RSSChannel.prototype.render = function(){
var output = "";
// output += "<h1>" + this.Title + "</h1>";
for (var rssItemId in this.RSSItems) {
var rssItem = this.RSSItems[rssItemId];
if (rssItem.IsValid != null && rssItem.IsValid())
output += rssItem.render();
}
return output;
};
function RSSItem(node){
this.Title = "";
this.Description = "";
this.Link = "";
this.Author = "";
if (node.childNodes == null)
return;
for (var i = 0; i < node.childNodes.length; i++) {
var childNode = node.childNodes.item(i);
switch (childNode.nodeName) {
case "description":
this.Description = getNodeText(childNode);
break;
case "title":
this.Title = getNodeText(childNode);
break;
case "link":
this.Link = getNodeText(childNode);
break;
case "author":
this.Author = getNodeText(childNode);
break;
}
}
}
RSSItem.prototype.render = function(){
var output = "<p>";
output += "<a target=\"_top\" href=\"" + this.Link + "\" title=\"" + this.Description + "\">";
output += this.Title;
output += "</a>";
output += "<br />";
output += "<i>by " + this.Author + "</i>";
output += "</p>";
return output;
};
RSSItem.prototype.IsValid = function(){
if (this.Title != null && this.Title != "")
return true;
};
getRss();
</script>
</div>
And there you have it. A simple RSS reader. Don't expect the code to parse all types of RSS feeds :).