Arne Roomann-Kurrik, OpenSocial Team
April 2008
Sometimes gadgets and OpenSocial applications need to work with data
located on remote servers. This functionality is very useful, but is not
as straightforward as making a simple AJAX request. Fortunately, the gadgets
API provides a function called makeRequest that allows you to
fetch data from and send data to remote servers.
This article provides an introduction to the various types of requests
that can be sent using makeRequest. Using this information,
you will be able to make your application send and
retrieve data outside of the bounds of the current container.
This article details the use of the makeRequest function in
the gadgets API. It assumes that you have beginner to intermediate
knowledge of the gadgets and OpenSocial APIs, and that you have written at
least one gadget.
If you do not meet these requirements, you may wish to take a look at these additional resources before continuing:
Because this is an introductory article, the topics of authentication and signed calls fall outside of the scope of discussion and will not be covered here. Future articles will explain these functions in detail.
Most sophisticated OpenSocial applications will need to send and receive data from third party servers. This functionality is particularly important for existing websites or social applications that want to make their site features available in OpenSocial containers. However, browser security models prevent gadget JavaScript from making cross-domain requests, meaning that developers cannot use standard AJAX libraries to fetch content from other sites.
Fortunately, the gadgets specification describes the
gadgets.io.makeRequest function which provides support for
gadgets to GET data from and POST data to third party sites. As you will
see in this article, makeRequest is very powerful. It can:
If you are experienced using the gadgets API, calling
makeRequest will be fairly straightforward. However, there are
many parameters that you may pass to makeRequest that affect
the function's behavior. This section will cover most of the different
types of requests that you can make.
The makeRequest signature looks like this:
gadgets.io.makeRequest(url, callback, opt_params)
There are three arguments to this function:
The opt_params argument to makeRequest should be
an object that contains one or more
gadgets.io.RequestParameters fields as keys.
These keys are:
The type of authentication to use in the request. Can be one of:
gadgets.io.AuthorizationType.NONEgadgets.io.AuthorizationType.SIGNEDgadgets.io.AuthorizationType.AUTHENTICATEDNote:
gadgets.io.AuthorizationType.AUTHENTICATED is
listed in the gadgets specification, but the specification does not define
which format this authenticated request must be in. This feature is
therefore not currently implemented on any container.
The type of content you want to treat the response as. Can be one of:
gadgets.io.ContentType.DOMgadgets.io.ContentType.FEEDgadgets.io.ContentType.JSONgadgets.io.ContentType.TEXTIf the content is a feed, whether to retrieve the summary fields for the
feed. Can be either true or false.
Headers to pass along in the request. Should be an object containing key/value pairs corresponding to the headers to be sent to the server. For example:
{
"Content-Type" : "application/atom+xml",
"Accept-Language": "en-us"
}
The HTTP method of the request. Can be one of:
gadgets.io.MethodType.GETgadgets.io.MethodType.POSTIf the content is a feed, the number of entries from the feed to retrieve. Should be specified as a number.
If the method is a POST, the raw POST data to pass to the other server. Should be specified as a string.
For example, to generate an opt_params object to set the
content-type of the request to JSON, you can use the following code:
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
gadgets.io.makeRequest("http://example.com", function() {}, params);
This would make a request to example.com and attempt to treat the returned content as a JSON encoded string. Note that the callback specified is an empty function— you would want to specify your own function to handle the content returned in the response.
When you receive a response from a makeRequest call, you are
returned a JavaScript object with the following structure:
{
data : <parsed data, if applicable>,
errors : <any errors that occurred>,
text : <raw text of the response>
}
The returned object has three properties:
[ "500 error" ]
This object will be supplied as the only argument to the callback function specified when the request was made.
The value you specify in the ContentType parameter to
makeRequest determines the sort of content you get back in the
response. This section covers the four available content types.
Text requests are the default content type for makeRequest
calls. Text content type requests do not make any attempt to parse the
content of the response.
You make a text content request like this:
function request() {
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] =
gadgets.io.ContentType.TEXT;
var url = "http://example.com";
gadgets.io.makeRequest(url, response, params);
};
If the request is successful, the response will come back with both the text and data fields populated with the raw response text.
function response(obj) {
/** obj.text contains the text of the page that was requested **/
};
If the content at http://example.com is the following:
<html>
<body>
<h1>Hello!</h1>
</body>
</html>
Then obj.text and obj.data fields will have the
following value:
<html>\n <body>\n <h1>Hello!</h1>\n </body>\n</html>
DOM requests attempt to retrieve a remote web page and parse it into a DOM document. You can use standard DOM manipulation/query methods on the resulting document to work with nodes in the response.
You can make a DOM request like this:
function request() {
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;
var url = "http://example.com";
gadgets.io.makeRequest(url, response, params);
};
If the request is successful, the response will come back with the raw
XML in the text field and the DOM object in the
data field.
function response(obj) {
/** obj.data contains a DOM element corresponding to the requested page **/
output(obj.data);
};
If the content at http://example.com is the following document:
<?xml version="1.0"?> <root> <title>Hello!</title> </root> </pre>
The obj.text property will have the following value:
<?xml version=\"1.0\"?>\n<root>\n <title>Hello!</title>\n</root>
The obj.data property will have the following general structure
(many of the attributes available on the returned XML document have been left
out for clarity).
{
...
nodeName : "#document",
childNodes : [
{
nodeName : "root",
childNodes : [
"\n",
{
nodeName : "title",
childNodes : [ "Hello!" ]
},
"\n"
]
}
],
...
}
JSON requests attempt to parse the response as a JSON encoded object. You can specify a JSON content type like this:
function request() {
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
var url = "http://example.com";
gadgets.io.makeRequest(url, response, params);
};
If the request is successful, then the response will come back with the raw
JSON string in the text field and the parsed JavaScript object
in the data field:
function response(obj) {
/** obj.data contains an object corresponding with the JSON-encoded response **/
output(obj.data);
};
If the content at http://example.com is the following:
{ "Message" : "Hello!", "Test" : [ "Item 1", "Item 2" ] }
The obj.text property will have the following value:
{ \"Message\" : \"Hello!\", \"Test\" : [ \"Item 1\", \"Item 2\" ] }
The obj.data property will have the following structure:
{
Message : "Hello!",
Test : [
"Item 1",
"Item 2"
]
}
Feed requests attempt to parse an ATOM or RSS xml feed and return the response as a JSON encoded object. You can specify a feed content type like this:
function request() {
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.FEED;
var url = "http://example.com";
gadgets.io.makeRequest(url, response, params);
};
If the request is successful, then the response will come back with the
raw XML text in the text field and the parsed JavaScript object
in the data field:
function response(obj) {
/** obj.data contains an object corresponding with the JSON-encoded response **/
output(obj.data);
};
If the content at http://example.com is the following:
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Feed</title>
<subtitle>A subtitle.</subtitle>
<link href="http://example.com/feed/" rel="self"/>
<link href="http://example.com/"/>
<updated>2008-03-12T18:30:02Z</updated>
<author>
<name>Sample Testington</name>
<email>stestington@example.com</email>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
<entry>
<title>You can request feeds with makeRequest</title>
<link href="http://example.com/entry12345"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2008-03-12T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>
</feed>
The obj.text property will be set to the text of the xml
document. The obj.data property will have the following
structure:
{
URL : "http://example.com",
Title : "Example Feed",
Description : "A subtitle.",
Link : "http://example.com/feed/",
Author : "Sample Testington",
Entry : [
{
Title : "You can request feeds with makeRequest",
Link : "http://example.com/entry12345",
Summary : "Some text."
}
}
makeRequest also allows you to choose between the HTTP GET and POST
methods.
Typically, you use GET to retrieve information from a website.
GET is the default mode for makeRequest, but you can explicitly
make a GET request with the following code:
function makeRequest(url) {
var params = {};
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
gadgets.io.makeRequest(url, response, params);
};
function response(obj) {
alert(obj.text);
};
makeRequest("http://example.com");
To pass parameters to your server in the GET request, just append them to the querystring when you make the request:
makeRequest("http://example.com?param1=12345¶m2=hello");
POST requests are typically used to pass data to a server with the intent to modify or delete records. You can pass larger amounts of data in a POST request than you would be normally able to with a GET request.
A POST can be performed using the following code:
function makeRequest(url, postdata) {
var params = {};
postdata = gadgets.io.encodeValues(postdata);
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.POST_DATA]= postdata;
gadgets.io.makeRequest(url, response, params);
};
function response(obj) {
output(obj.text);
};
var data = {
data1 : "test",
data2 : 123456
};
makeRequest("http://example.com", data);
In addition to specifying the METHOD in the opt_params for
this request, you should specify a parameter under the key
gadgets.io.RequestParameters.POST_DATA. The default encoding
for POST is application/x-www-form-urlencoded which means that
the value for the POST_DATA parameter should be a series of urlencoded
key/value pairs joined with ampersands (&). To make converting objects
of data into this format easier, the function
gadgets.io.encodeValues is provided. encodeValues
accepts a JavaScript object and returns an encoded string suitable for the
POST_DATA parameter.
For example, running:
var data = {
data1 : "test",
data2 : 123456
};
gadgets.io.encodeValues(data);
produces the string:
data1=test&data2=123456
This string can be passed directly as the value for
gadgets.io.RequestParameters.POST_DATA.
Depending on the container you are developing for, makeRequest
calls may be cached. This is great for reducing the load that your servers
have to be able to handle, since OpenSocial applications may have millions of
users, each requesting data from your server. However, this caching of
makeRequest calls can wind up displaying old data to users if the content
returned by your server changes often.
If you expect the content at the URL you are fetching to change frequently, you may want to work around the automatic caching with a more finely grained method.
The following code defines a function named makeCachedRequest.
This function takes the same parameters as makeRequest, but
accepts one additional parameter named refreshInterval, which
should be specified as the number of seconds that should elapse before
the makeRequest call requests content from your servers again.
For example, specifying 10 will mean that the container would only query the
supplied URL once every 10 seconds, no matter how many people are using your
application. Specifying 0 will query your server each time the function is
called, effectively bypassing the cached version.
This function gives you more control over how frequently your content is
cached. You can tweak the refreshInterval parameter to optimize
request caching for your application's specific request patterns.
function makeCachedRequest(url, callback, params, refreshInterval) {
var ts = new Date().getTime();
var sep = "?";
if (refreshInterval && refreshInterval > 0) {
ts = Math.floor(ts / (refreshInterval * 1000));
}
if (url.indexOf("?") > -1) {
sep = "&";
}
url = [ url, sep, "nocache=", ts ].join("");
gadgets.io.makeRequest(url, response, params);
};
The way this method works is that it appends a timestamp to each request
URL before actually requesting the data, meaning that each millisecond
that passes results in a new unique URL that does not exist in the
makeRequest cache. Specifying refreshInterval
divides this timestamp by the number of seconds between requests so that
a unique
URL is generated only once every X seconds, where X is the
value of refreshInterval.
You can call makeCachedRequest in places where you would use
gadgets.io.makeRequest— just change the function name and
put the extra parameter at the end of the arguments you would normally use.
This article should have given you a good understanding of the different
ways to use makeRequest. For more information about some
of the topics discussed here, check the following resources: