JSONP原理与使用

http://www.ibm.com/developerworks/library/wa-aj-jsonp1/

AJAX可以用来在后台获取数据,而不干扰前台的web显示。它通过使用XMLHttpRequest来使得客户端的JavaScript可以与服务器建立http连接。但是根据同源策略,这种方法不能获取跨域名的数据,如果尝试从一个不同的域名下请求数据,将会返回一个安全错误。同源策略禁止一个脚本从一个域获取或操作另一个域上的文档,即请求的URL必须是与当前的页面同域名的。这项浏览器的策略可以追溯到Netscape Navigator 2.0.

一个相对简单的方法是当前页面所在的服务器作为一个代理中继,该页面向自己的服务器请求数据,而服务器再向第三方的服务器请求需要的数据。另一种方法是使用框架元素在当前页面中创建新的areas,然后利用GET请求获取需要的第三方数据,在Frame中的数据内容就符合同源策略。

更有效的方法是在页面中插入一个动态的script元素。该script的src指向第三方域的URL,并在sript中返回需要的数据。一旦脚本文件被加载,它就会被执行。

JSON是一个轻量级的数据格式用来在浏览器和服务器之间交换数据。JSON实际上是用字符串string来表示一个JavaScript对象。例如有一个股票对象,它有两个属性符号和价格。在JavaScript中采用如下的方法来定义这个对象:

var ticker = {symbol: 'IBM', price: 91.42};

使用JSON表示这个对象如下:

{symbol: 'IBM', price: 91.42}

定义一个函数来显示股票价格:

function showPrice(data) {

   alert("Symbol: " + data.symbol + ", Price: " +data.price);

}

通过向这个函数传递JSON数据作为参数:

showPrice({symbol: 'IBM', price: 91.42});// alerts: Symbol: IBM, Price: 91.42

当把这两段代码放入同一个页面中后:

<scripttype="text/javascript">

function showPrice(data) {

   alert("Symbol: " + data.symbol + ", Price: " +data.price);

}

</script>

<scripttype="text/javascript">showPrice({symbol: 'IBM', price:91.42});</script>

当页面加载完毕将会有提示信息。

下面开始使用动态JavaScript插入的方法

showPrice({symbol: 'IBM', price: 91.42});// alerts: Symbol: IBM, Price: 91.42

存入一个单独的JavaScript文件中,如help.js。

在一个页面中加入如下代码:

<scripttype="text/javascript">

// This is ourfunction to be called with JSON data

function showPrice(data){

    alert("Symbol: " + data.symbol +", Price: " + data.price);

}

var url = “help.js”;// URL of the external script

// this showsdynamic script insertion

var script =document.createElement('script');

script.setAttribute('src',url);

// load the script

document.getElementsByTagName('head')[0].appendChild(script);

</script>

在页面中动态插入help.js后,将会自动执行其中的JavaScript代码,使用实际的help.js文件中返回的数据作为参数来调用showPrice()函数。

由于同源策略不阻止动态插入script元素,所以就可以在页面中以插入不同域的带有JSON数据的JavaScript程序。JSONP(JSON with Padding):JSON data wrapped in a function call. 需要注意的是,在页面中必须在插入JavaScript代码前预先定义好回调函数,并在回调函数中对得到的JSON数据进行处理,例如此处的showPrice()。

JSONP服务就是指服务器支持将返回的JSON数据封装到涌入自定义的函数调用中的服务。这种方法依赖于远程服务接收一个回调函数名作为参数,服务器产生一个该函数的调用,并将返回的JSON数据作为它的参数,当到大客户端时,这个函数就会被执行。

JQuery 的JSONP支持

JQuery从1.2版本开始原生的支持JSONP调用。如果按照url?callback=?的语法指定一个JSONP回调函数,就可以加载一个跨域的JSON数据。JQuery可以自动的将?替换为一个生成的函数名的调用。

jQuery.getJSON(url+"&callback=?", function(data) {    alert("Symbol: " + data.symbol + ", Price: " + data.price);});

JQuery向window对象添加了一个全局函数,这个函数在脚本被插入后就被调用。当完成后,这个函数就被删除。同时JQuery还对非跨域的调用进行优化,如果是在同一个域名下,就会使用普通的AJAX请求。

JSONP服务举例:

首先假设服务器在URL请求中接受一个参数名为callback,同时假设对JSONP服务的请求如下:

http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=showPrice

symbol是一个表示股票代码的请求参数,callback参数指出了客户端页面中回调函数的名字。在JQuery中可以采用如下的方式发送请求。

jQuery.getJSON("http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=?", function(data) {    alert("Symbol: " + data.symbol + ", Price: " + data.price);});

在这里使用?作为回调函数的名称,而不是实际定义的回调函数名。因为JQuery会自动将?替换为一个函数名(如 jsonp1232617941775),该函数会调用其中的内联函数。这样用户不用在定义具体的函数如showPrice()函数。

下面的代码说明如何使用PHP实现JSONP服务:

$jsonData = getDataAsJson($_GET['symbol']);echo $_GET['callback'] . '(' . $jsonData . ');';// prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});

下面的代码说明如何使用Java实现JSONP服务:

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)   throws ServletException, IOException {         String jsonData = getDataAsJson(req.getParameter("symbol"));         String output = req.getParameter("callback") + "(" + jsonData + ");";          resp.setContentType("text/javascript");                   PrintWriter out = resp.getWriter();         out.println(output);         // prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});}

mashup是糅合,是当今网络上新出现的一种网络现象,将两种以上使用公共或者私有数据库的web应用,加在一起,形成一个整合应用。 现在假如你是一个Web开发者,你通过一个API 找到你附近哪些地方会有犯罪。然后你访问Google 地图API,把这两个内容整合在一起,那么你就得到了一个标有犯罪纪录的地图。这个新的地图就叫Mashup。因为开发者通过来自多个网站的APIs,把他们合并在一起,成为了一个新的很cool的应用程序。

豆瓣网的JavaScript客户端实现:

http://www.douban.com/js/api.js?v=2

一个简单的JSONP封装:

functionload_script(url, callback) {

        // 获取head标签

       var head =document.getElementsByTagName('head')[0];

        // 创建script标签

       var script =document.createElement('script');

        // 指定script类型

       script.type = 'text/javascript';

        // 需要ajax调用的url

       script.src = url;

       // 借鉴了jQuery的script跨域方法

       //侦听script的onload、onreadystatechange事件,并执行回调函数(取数据、处理数据的操作)

       script.onload = script.onreadystatechange= function() { 

              if ((!this.readyState ||this.readyState === "loaded" || this.readyState ==="complete")) { 

                     //调用回调函数,这样写的好处是:如果回调函数不存在,不会抛异常

                     callback &&callback();

                      // Handle memory leak in IE

                     script.onload =script.onreadystatechange = null;

                      // 删除掉创建的script标签

                     if (head &&script.parentNode) {

                            head.removeChild(script);

                     }

              }

       };

       // Use insertBefore instead ofappendChild to circumvent an IE6 bug.

       // 创建script标签,发起ajax调用

       head.insertBefore(script,head.firstChild);

//直接访问http://suggestion.baidu.com/su?wd=ww 可以看到返回的结果为:

//window.baidu.sug({q:"ww",p:true,s:["wwe","www.baidu.com","www.163.com","www.taobao.com","www.qq.co//m","www.4399.com","www.126.com","www.51.com","www.hao123.com","www.youku.com"]});

//1.假设想取的数据为sug里的s部分的数组

//2.那么在成功返回请求结果后,上面的window.baidu.sug(....)实际上就是调用了下面已经定义好的sug方法

//其中,data表示形参,对应着sug里的{q:....}部分的数据

//3.因此,通过data.s便可以获取到["wwe"...]部分的数组

//4.仅此而已...

window.baidu= {

       sug : function(data) {

              alert(data.s);

       }

};

 load_script('http://suggestion.baidu.com/su?wd=w',function() {

       alert('loaded');

});

豆瓣网的JavaScript客户端实现:

http://www.douban.com/js/api.js?v=2

另一篇文章里介绍了如何使用YQL和JQuery使用JSONP的方式来获取APIs提供的数据。 

http://www.ibm.com/developerworks/web/library/wa-aj-jsonp2/index.html

以下代码实现了使用YQL利用flickr的API进行图片搜索,并根据图片信息合成图片的地址。

var yqlUrl3="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20flickr.photos.search%20where%20text%3D%22apple%22%20limit%2030&format=json&diagnostics=true&callback=?";

    var count = 0;

    $.getJSON(yqlUrl3, function(data){

    $.each(data.query.results.photo, function(index, item){  

    var imgId = item.id;

    var farm = item.farm;

    var server = item.server;

    var secret = item.secret;

    var title = item.title;

    var owner = item.owner;

    var turl ="http://www.flickr.com/photos/"+owner + "/"+imgId;

    // construct the url of the images

    var imgurl = "http://farm"+farm+".static.flickr.com/"+server+"/"+imgId+"_"+secret+".jpg";

    $('#weather').append(

    $('<p/>').append($('<span class="img_title"/>').text(++count+"."+title).append($("<br>"))

    .append($("<a href='"+turl+"' target=_blank/>")

    .append($("<img src='"+imgurl+"' alt="+title+"/>")

    ))));

});

    });

//Because the search API doesn't return the url of the images, this post introduce  how to get the image's url from  its information without url. http://developer.yahoo.com/blogs/ydn/posts/2010/04/building_flickr_urls_from_yql_flickrphotossearch_results/ 

本文使用Blog_Backup未注册版本导出,请到soft.pt42.com注册。

posted @ 2011-03-26 01:15  莫忆往西  阅读(334)  评论(0)    收藏  举报