爲何是 JSONP

AJAX、JSON、JSONP

在 WEB 開發中,常常見到諸如 AJAX、JSON、JSONP 這些名詞。三者看起來很像,不少同窗尤爲是沒有系統瞭解過前端技術體系的同窗,日常只是藉助相似 JQuery 這類庫封裝好的函數使用而已,並不瞭解其原理。但這三種東西具體是什麼,有什麼關係和區別卻經常說不清楚。javascript

接下來,會簡要介紹一下三者的的含義,重點闡述 JSONP 的來源和原理,以及爲何 JSONP 不是 AJAX。html

AJAX (Asynchronous JavaScript + XML)

Ajax isn’t a technology. It’s really several technologies, each flourishing in its own right, coming together in powerful new ways. Ajax incorporates:前端

  • standards-based presentation using XHTML and CSS;java

  • dynamic display and interaction using the Document Object Model;web

  • data interchange and manipulation using XML and XSLT;ajax

  • asynchronous data retrieval using XMLHttpRequest;json

  • and JavaScript binding everything together.跨域

異步 JavaScript + XML,是在 2005 年由 Jesse James Garrett 提出的一個術語。 AJAX 並不是特指某種技術, 描述的是一種結合使用大量已有技術的方式, 包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 還有最重要的 XMLHttpRequest 對象.數組

儘管在 AJAX 中 X 表明 XML, 但如今更多的在使用 JSON,由於 JSON 具備不少優點,好比更輕量而且是 JavaScript 的一部分。在 AJAX 模型中 JSON 和 XML 都用於承載信息.瀏覽器

JSON(Javascript Object Notation)

JSON 是一種輕量級的數據交換格式。由道格拉斯·克羅克福特(Douglas Crockford)在 2012 年發明,並逐漸取代 XML 成爲事實上的數據交換格式標準。

JSON 基於 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。但採用徹底獨立於語言的文本格式,並使用了相似於 C 語言家族的習慣。

在 JSON 中,一共 6 種數據類型:

  • number:跟 Javascript 的數值一致,除去不曾使用的八進制與十六進制格式,和一些編碼細節

  • boolean:truefalse

  • string:是由雙引號包圍的任意數量Unicode字符的集合,使用反斜線轉義

  • null:null

  • array:數組是值(value)的有序集合。一個數組以「[」(左中括號)開始,「]」(右中括號)結束,值之間使用「,」(逗號)分隔

  • object:對象是一個無序的「‘名稱/值’對」集合。一個對象以「{」(左括號)開始,「}」(右括號)結束,每一個「名稱」後跟一個「:」(冒號);「‘名稱/值’ 對」之間使用「,」(逗號)分隔

以及上面的任意組合。

在 JavaScript 中有一個全局對象 JSON,包含兩個方法 JSON.stringify()JSON.parse(),用於序列化和解析 JSON。

固然也有人使用 eval("(" + string + ")") 代替 JSON.parse() 來 解析JSON,相比而言這種方式的容錯性更高。

JSONP(JSON with Padding)

由於 XMLHttpRequest 有同源策略,而在實際開發中又經常會有跨域的需求,早期開發者爲了解決跨域問題而搞出來這樣一個頗爲奇怪的東西。產生緣由和名字同樣古怪,光聽名字恐怕沒幾我的知道說的是個什麼東西。

恰好 <script> 標籤能夠引用其餘域下的靜態資源,想一想咱們有時候在站點引入的數據統計類的 JS。

但咱們要的是數據,而不是一段靜態的 JS 代碼,怎麼辦?

這還不簡單嗎,讓服務器動態生成 js ,再把數據放進去不就能夠嗎。爲了區分每一份數據,還須要針對返回的數據作一個標識,其實就是在數據外面包裹一個函數名。

而後須要瀏覽器端預先設置好這樣一個函數,返回的數據就至關於一次執行過程,對獲取數據的處理。

總結

  1. AJAX 是一類技術的集合,其中最重要的是 XMLHttpRequest

  2. JSON 是一個數據交換格式,也是目前事實上的標準

  3. JSONP 是爲解決跨域問題搞出來的一種獲取數據的方式

舉個栗子

接下來,用這個簡單的示例來講明如何經過 JSONP 的方式獲取數據,以及它究竟是怎樣工做的。

服務器

這裏使用 Node.js 返回一段簡單的數據。

/**
 * 一個簡單的 http 服務器,返回 json 數據
 * 跟 Node 主頁上的那個經典例子沒太大差異
 */

var http = require('http');
var urllib = require('url');

var host = '127.0.0.1';
var port = 9999;

var data = {'name': 'Mirreal', 'age': '24'};

http.createServer(function(req, res) {
  var params = urllib.parse(req.url, true);

  if (params.query && params.query.callback) {

    var str =  params.query.callback + '(' + JSON.stringify(data) + ')'; // jsonp
    res.writeHead(200, { 'Content-Type': 'application/javascript' });
    res.end(str);
  } else {
    res.end(JSON.stringify(data)); // 普通的json
  }

}).listen(port, host, function() {
  console.log('server is listening on port ' + port);
});

瀏覽器

// zepto 的寫法
$.ajax({
  type: 'GET',
  url: 'http://127.0.0.1:9999',
  data: { _input_charset: 'utf-8' },
  dataType: 'jsonp',
  timeout: 300,
  context: $('body'),
  success: function(data){
    console.log(data)
  },
  error: function(xhr, type) {
    console.log('Ajax error!')
  }
});

這樣就很輕鬆的經過 JSONP 的方式獲取到數據,咱們彷佛也不須要關內心面到底是怎麼一回事。但有時候肯能會有人問起:「爲何 jsonp 不能使用 POST 方法」的問題,其實稍微瞭解一下 JSONP 的原理,這種問題徹底就不存在了。

雖然像 JQuery 這類庫將 JSONP 封裝到 $.Ajax() 上,但準確來說是不對的。由於 JSONP 只是經過動態地經過 <script> 標籤去請求一段 JS 代碼(或者叫數據),而非使用 XMLHttpRequest ,原理就像下面這樣:

對 JSONP 的簡單封裝

/**
 * 對 JSONP 的一種簡單封裝
 *
 * @param {Object} options
 * @returns null
 */
function getJsonp(options) {

  var callbackName = options.callbackName;
  var url = options.url;


  var scriptElem = document.createElement('script');
  scriptElem.setAttribute('src', url + '?callback=' + callbackName);

  scriptElem.onload = function(e) {
    delete window[callbackName];
    this.parentNode.removeChild(this);
  };

  scriptElem.onerror = function(e) {
    console.log(e, 'load error');

    delete window[callbackName];
    this.parentNode.removeChild(this);
  };

  window[callbackName] = options.success;

  // 調用
  document.querySelector('head').appendChild(scriptElem);
}

這段代碼對 JSONP 進行一層簡單包裝,調用也很簡單:

getJsonp({
  'url': 'http://127.0.0.1:9999/',
  'callbackName': 'log',
  'success': function(data) {
     console.log('我是回調函數,我拿到數據了', data);
  }
});

看上去代碼還挺長的,實際上核心代碼很少,分三步:

1.建立一個 <script> 標籤,並設置其 url

var scriptElem = document.createElement('script');
scriptElem.setAttribute('src', url + '?callback=' + callbackName);

2.設置回調函數

window[callbackName] = options.success;

這裏簡單處理,直接把傳入的回調函數設置成全局的

3.調用

document.querySelector('head').appendChild(scriptElem);

實際上就是把 <script> 加到 html 文檔中,這樣就會去加載標籤的內容,也就是一個 JS 文件。

但一般現實中跑的代碼內容會更多,包含一些錯誤控制、參數拼接、超時處理、性能安全等方面的,但它仍然清楚地描述 JSONP 的原理。

安全

早期的瀏覽器處於安全層面的考量,制定同源策略,限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。

可是隨着互聯網的發展催生了跨域訪問進行數據交互的需求,因而 JSONP 就產生了,以及後來的 CORS 機制,容許 XMLHttpRequest 對象發起跨域的請求。

可是另外一方面,也增長了安全風險,咱們在使用的時候應當更加謹慎當心,防止 XSS、CSRF 等攻擊。

其餘

數據預覽

以前碰到一個問題,在調用一些接口返回的數據沒法使用 Chrome 預覽,本身寫測試接口的時候也碰到過。後來發現,只是由於沒有在 response 頭部加上 Content-Type: application/javascript,僅此而已。

相關文章
相關標籤/搜索