This is where JSONP comes in. JSONP stands for JSON with Padding. While XMLHttpRequest
traffic is restricted to the server of origin, there is no such restriction on
from where you can load JavaScript source by using the standard <script>
tag. Most developers know that you can load jQuery libraries from a CDN by pointing
your <script> "src" attribute to the CDN URL for the given JavaScript
library. It doesn't matter where the domain is - the script will load just fine.
This "security hole", if you will -- has been used for years by major
players including Google for Adsense, Yahoo and many others, and is therefore
not likely to be changed anytime soon. In fact, it was used when AJAX was originally
called "Remote Scripting" - which is as early as 1998, when it was
used by Microsoft in Outlook Web Access.
How it works
What JSONP does is that instead of using XMLHttpRequest, it will dynamically create
a <script> tag to get the data. A <script> tag does not have to
return JavaScript -- it will load whatever is returned from the URL specified
as its “src” attribute.
Instead of specifying the <script> src attribute as:
http://www.server.com/methodname
JSONP appends a parameter named callback to the URL, so we have this as the src attribute:
http://www.server.com/methodname?callback=mycallback
JSONP requests are not dispatched using the XMLHTTPRequest at all; instead a <script>
tag is created whose source is set to the target URL. This script tag is then
added to the DOM -- normally the <head>. The advantage of doing this with the jQuery library is that everything is standardized and you don't have to worry about "plumbing" at all.
A web service or API that supports the JSONP protocol will see this callback attribute
on the request url and instead of just returning the JSON data as before, it
will wrap the data with a method call of the same name specified by this callback
parameter. So the data the web service returns will now look like this:
mycallback({[..JSON data here..]})
After the data loads, we end up with a <script> tag in the page that looks
like this:
<script>
mycallback({[..JSON data here..]})
</script>
Since this is done dynamically in the receiving page, the new script executes, immediately
making the call to the callback method, supplying the JSON data, which "materializes"
the JSON object because it is legal javascript. This is similar to the callbacks
that you are able to provide to a traditional Ajax XMLHttpRequest method.
With jQuery, which is widely used by developers now, the JSONP method call takes
this form:
$.ajax({
dataType: 'jsonp',
data: 'id=100',
jsonp: 'jsonpcallback',
url: 'http://www.server.com/methodname',
success: functionName,
error: otherfunctionName
});
The advantage of giving it a name - JSONP - and specifying exactly how it is to
be used is for the sake of conformity and standards. If you ask some vendor if
their API supports JSONP and they say "yes", you can be pretty confident
that it is a standard implementation.
jQuery also supports a $getJSON function:
$.getJSON(url + "&jsoncallback=?", function(data){
Name = data.Name;
Message = data.Message;
if(Name != "" && Message != "")
{
jQuery("#divMsg").html("Hello " + Name + ", you typed:
" + Message);
}
else
{
jQuery("divMsg").html("Please enter Name and Message to get the
result!!!");
}
})
Note that with the $getJSON function, the callback is specified on the URL as "&jsoncallback=?".
jQuery is smart enough to construct a dynamic callback function with a unique
name, and this is sent to the server.
In either case, this must be an HTTP GET verb call. JSONP does not work with POST at all.
Let's use a working example to see how this all fits together. You can copy all the
code after this sentence, put it into Notepad, and save it in c:\inetpub\wwwroot\
as JqueryJson.html, and request it with your browser at http://localhost/JqueryJson.html
and it should work "out of the box":
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Flickr API With JSONP</TITLE>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"> </script>
<script type="text/javascript">
$(document).ready(function(){
var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne";
$.ajax({
url: jsonFeed,
data: { "lang" : "en-us", "format" : "json",
"tags" : "sunset" },
dataType: "jsonp",
jsonp: "jsoncallback",
timeout: 5000,
success: function(data, status){
$.each(data.items, function(i,item)
{
$("<img>").attr("src", (item.media.m).replace("_m.","_s.")).attr("alt",
item.title).appendTo("ul#flickr").wrap("<li><a href=\"" + item.link + "\"></a></li>");
if (i == 10) return false; // don't go crazy
});
},
error: function(XHR, textStatus, errorThrown){alert("ERROR: " + textStatus);
}
});
});
</script>
</HEAD>
<BODY>
<ul id=flickr>
</ul>
<xmp>
Call: http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=jsonp1292276431678&lang=en-us&format=json&tags=sunset
Results:
jsonp1292276431678({
"title": "Recent Uploads tagged sunset",
"link": "http://www.flickr.com/photos/tags/sunset/",
"description": "",
"modified": "2010-12-13T21:20:20Z",
"generator": "http://www.flickr.com/",
"items": [
{
"title": "",
"link": "http://www.flickr.com/photos/alycassata/5258391795/",
"media": {"m":"http://farm6.static.flickr.com/5046/5258391795_efb400116f_m.jpg"},
"date_taken": "2010-12-11T13:30:19-08:00",
"description": " <p><a href=\"http://www.flickr.com/people/alycassata/\">alycassata<\/a>
posted a photo:<\/p> <p><a href=\"http://www.flickr.com/photos/alycassata/5258391795/\" title=\"\"><img
src=\"http://farm6.static.flickr.com/5046/5258391795_efb400116f_m.jpg\"
width=\"160\" height=\"240\" alt=\"\" /><\/a><\/p>
<p>+2 without editing in comments!<\/p>",
"published": "2010-12-13T21:20:20Z",
"author": "nobody@flickr.com (alycassata)",
"author_id": "36599621@N03",
"tags": "park new york city sunset white ny black brooklyn canon eos
rebel 50mm model photoshoot f14 desaturated dalton aly fischer cassata 550d t2i
alycassata"
},
{
"title": "LA Sunset",
"link": "http://www.flickr.com/photos/bdu/5258380431/",
"media": {"m":"http://farm6.static.flickr.com/5003/5258380431_149e0198c0_m.jpg"},
"date_taken": "2010-12-11T17:56:39-08:00",
"description": " <p><a href=\"http://www.flickr.com/people/bdu/\">bdu<\/a> posted
a photo:<\/p> <p><a href=\"http://www.flickr.com/photos/bdu/5258380431/\" title=\"LA
Sunset\"><img src=\"http://farm6.static.flickr.com/5003/5258380431_149e0198c0_m.jpg\"
width=\"240\" height=\"161\" alt=\"LA Sunset\"
/><\/a><\/p> ",
"published": "2010-12-13T21:16:19Z",
"author": "nobody@flickr.com (bdu)",
"author_id": "54581307@N00",
"tags": "sunset losangeles griffithobservatory"
},
.....
]
})
</xmp>
</BODY>
</HTML>
I have also provided a standard ASP.NET Visual Studio 2010 Solution that you can download with the page included. Bear in mind that everything in the ASPX Default page is client-side, but it illustrates
how easily one can integrate jQuery, AJAX and JSONP into a standard ASP.NET application.