JSONP - 一种通过json传递数据的跨域方式

Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

为什么我们从不同的域(网站)访问数据需要一个特殊的技术( JSONP )呢?这是因为同源策略。

同源策略,它是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。

下面來循序漸進的說明一下JSONP在客戶端的實現:

我們知道,哪怕跨域js文件中的代碼(當然指符合web腳本安全策略的),web頁面也是可以無條件執行的。

遠程服務器remoteserver.com根目錄下有個remote.js文件代碼如下:

alert('我是遠程文件');

本地服務器localserver.com下有個jsonp.html頁面代碼如下:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
  </head>
  <body></body>
</html>

毫無疑問,頁面將會彈出一個提示窗體,顯示跨域調用成功。

现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。 jsonp.html页面代码如下:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript">
      var localHandler = function(data){
          alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
      };
    </script>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
  </head>
  <body></body>
</html>

remote.js文件代碼如下:

localHandler({
  "result":"我是远程js带来的数据"
});

運行之後查看結果,頁面成功彈出提示窗口,顯示本地函數被跨域的遠程js調用成功,並且還接收到了遠程js帶來的數據。很欣喜,跨域遠程獲取數據的目的基本實現了,但是又一個問題出現了,怎麽讓遠程js知道它應該調用的本地函數叫什麽名字呢?畢竟是JSONP的服務者都要面對很多服務對象,而這些服務對象各自的本地函數都不相同啊?我們接著往下看。

聰明的開發者很容易想到,只要服務端提供的js腳本是動態生成的就行了呗,這樣調用者可以傳一個參數過去告訴服務端“我想要一段調用XXX函數的js代碼,請你返回給我”,于是服務器就可以按照客戶端的需求來生成js腳本並響應了。

看jsonp.html頁面的代碼:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript">
      // 得到航班信息查询结果后的回调函数
      var flightHandler = function(data){
          alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
      };
      // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
      var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
      // 创建script标签,设置其属性
      var script = document.createElement('script');
      script.setAttribute('src', url);
      // 把script标签加入head,此时调用开始
      document.getElementsByTagName('head')[0].appendChild(script);
    </script>
  </head>
  <body></body>
</html>

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是JSONP客户端实现的核心部分,本例中的重点也就在于如何完成JSONP调用的全过程。 我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。 OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flightHandler({
  "code": "CA1998",
  "price": 1780,
  "tickets": 5
});

總結原生JS實現JSONP的步驟

  1. 定義獲取數據後調用的回調函數
  2. 動態生成對服務端JS進行引用的代碼

  3. 設置url爲提供jsonp服務的url地址,並在該url中設置相關callback參數
  4. 創建script標簽,並設置其src屬性
  5. 把script標簽加入head,此時調用開始。

  6. 服务端: 将客户端发送的callback参数作为函数名来包裹住JSON数据,返回数据至客户端。