為什么是 JSONP

AJAX、JSON燃异、JSONP

在 WEB 開發(fā)中携狭,經(jīng)常見到諸如 AJAX、JSON回俐、JSONP 這些詞逛腿,但這三種東西到底是什么,又有什么關(guān)系和區(qū)別仅颇。

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;
  • dynamic display and interaction using the Document Object Model;
  • data interchange and manipulation using XML and XSLT;
  • asynchronous data retrieval using XMLHttpRequest;
  • and JavaScript binding everything together.

異步 JavaScript + XML, 本身不是一種技術(shù), 是在2005年由 Jesse James Garrett 提出的一個術(shù)語, 描述了一種結(jié)合使用大量已有技術(shù)的方式, 包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 還有最重要的 XMLHttpRequest 對象.

盡管在 AJAX 中 X 代表 XML, 但現(xiàn)在 JSON 使用的更多单默,因為 JSON 具有很多優(yōu)勢,比如更輕量并且是 JavaScript 的一部分. 在 AJAX 模型中 JSON 和 XML 都用于承載信息.

JSON(Javascript Object Notation)

JSON 是一種輕量級的數(shù)據(jù)交換格式忘瓦。由道格拉斯·克羅克福特(Douglas Crockford)在 2012 年發(fā)明搁廓,并逐漸取代 XML 成為事實上的數(shù)據(jù)交換格式標準。

JSON 基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集耕皮。但采用完全獨立于語言的文本格式境蜕,并使用了類似于 C 語言家族的習慣。

在 JSON 中凌停,一共 6 種數(shù)據(jù)類型:

  • number:跟 Javascript 的數(shù)值一致粱年,除去未曾使用的八進制與十六進制格式,和一些編碼細節(jié)
  • boolean:truefalse
  • string:是由雙引號包圍的任意數(shù)量Unicode字符的集合罚拟,使用反斜線轉(zhuǎn)義
  • null:null
  • array:數(shù)組是值(value)的有序集合台诗。一個數(shù)組以“[”(左中括號)開始完箩,“]”(右中括號)結(jié)束,值之間使用“,”(逗號)分隔
  • object:對象是一個無序的“‘名稱/值’對”集合拉庶。一個對象以“{”(左括號)開始嗜憔,“}”(右括號)結(jié)束秃励,每個“名稱”后跟一個“:”(冒號)氏仗;“‘名稱/值’ 對”之間使用“,”(逗號)分隔

以及上面的任意組合。

在 javascript 中有一個全局的對象 JSON夺鲜,包含兩個方法 JSON.stringify()JSON.parse()皆尔,用于序列化和解析 JSON。

JSONP(JSON with Padding)

最初是開發(fā)者為了解決跨域問題搞出來的一個頗為奇怪的東西币励,產(chǎn)生原因和名字一樣古怪慷蠕,光聽名字恐怕沒幾個人知道說的是個什么東西。

因為 ajax 請求收到同源策略的限制不允許跨域訪問食呻,而在實際開發(fā)中又常常會有類似的需求流炕。

剛好 <script> 標簽可以引用其他站點的靜態(tài)資源,想想我們有時候在站點引入的數(shù)據(jù)統(tǒng)計類的 js仅胞。

但我們要的是數(shù)據(jù)每辟,而不是一段靜態(tài)代碼,怎么辦干旧?

這還不簡單嗎渠欺,讓服務器動態(tài)生成 js ,再把數(shù)據(jù)放進去不就可以嗎椎眯。為了區(qū)分不數(shù)據(jù)挠将,還需要針對返回的數(shù)據(jù)做一個標識,其實就是在數(shù)據(jù)外面包裹一個函數(shù)名编整。

然后需要瀏覽器端預先設(shè)置好這樣一個函數(shù)舔稀,返回的數(shù)據(jù)就相當于一次執(zhí)行過程,對獲取數(shù)據(jù)的處理掌测。

總結(jié)

  1. AJAX 是一類技術(shù)的集合内贮,其中最重要的是 XMLHttpRequest
  2. JSON 是一個數(shù)據(jù)交換格式
  3. JSONP 是為了解決跨域問題搞出來的一種獲取數(shù)據(jù)的方式

下面舉個栗子

服務器

這里使用 node 返回一段簡單的數(shù)據(jù)。

/**
 * 一個簡單的 http 服務器,返回 json 數(shù)據(jù)
 * 跟 node 主頁上的那個經(jīng)典例子沒太大差別
 */

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 的方式獲取到數(shù)據(jù)赏半,我們也不需要關(guān)心里面究竟是怎么一回事贺归,但經(jīng)常會有人問起像“為什么 jsonp 不能使用 POST 方法”的問題,其實稍微了解一下 JSONP 的原理断箫,這種問題完全就不存在了拂酣。

雖然像 jQuery 這類庫將 jsonp 封裝到 Ajax 上,但準確來講是不對的仲义。因為 jsonp 只是通過動態(tài)地通過 <script> 標簽去請求一段 js 代碼(或者叫數(shù)據(jù))婶熬,而非使用 XMLHttpRequest 剑勾,原理就像下面這樣:

/**
 * 對 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;

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

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

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

看上去代碼還挺長的赵颅,實際上核心代碼不多虽另,分三步:

1.創(chuàng)建一個 <script> 標簽,并設(shè)置其 url

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

2.設(shè)置回調(diào)函數(shù)

window[callbackName] = options.success;

這里簡單處理饺谬,直接把傳入的回調(diào)函數(shù)設(shè)置成全局的

3.調(diào)用

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

實際上就是把 <script> 加到 html 文檔中捂刺,這樣就會去加載標簽的內(nèi)容,也就是一個 js 文件募寨。

但通匙逭梗現(xiàn)實中跑的代碼內(nèi)容會更多,包含一些錯誤控制拔鹰、參數(shù)拼接仪缸、超時處理而克、性能安全等方面的礁击,但它仍然清楚地描述 JSONP 的原理。

安全

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

但是隨著互聯(lián)網(wǎng)的發(fā)展催生了跨域訪問進行數(shù)據(jù)交互的需求,于是 JSONP 就產(chǎn)生了决采,以及后來的 CORS 機制自沧,允許 XMLHttpRequest 對象發(fā)起跨域的請求。

但是另一方面树瞭,也增加了安全風險拇厢,我們在使用的時候應當更加謹慎小心,防止 XSS晒喷、CSRF 等攻擊孝偎。

其他

數(shù)據(jù)預覽

之前碰到一個問題,為什么調(diào)用一些接口返回的數(shù)據(jù)無法使用 Chrome 預覽凉敲,我自己寫測試接口的時候也碰到過衣盾。其實這里完全沒有什么技術(shù)可言,只是因為沒有在 response 頭部加上 Content-Type: application/javascript爷抓,僅此而已势决。

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蓝撇,隨后出現(xiàn)的幾起案子果复,更是在濱河造成了極大的恐慌,老刑警劉巖渤昌,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虽抄,死亡現(xiàn)場離奇詭異走搁,居然都是意外死亡,警方通過查閱死者的電腦和手機迈窟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門私植,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人车酣,你說我怎么就攤上這事曲稼。” “怎么了骇径?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵躯肌,是天一觀的道長者春。 經(jīng)常有香客問我破衔,道長,這世上最難降的妖魔是什么钱烟? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任晰筛,我火速辦了婚禮,結(jié)果婚禮上拴袭,老公的妹妹穿的比我還像新娘读第。我一直安慰自己,他們只是感情好拥刻,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布怜瞒。 她就那樣靜靜地躺著,像睡著了一般般哼。 火紅的嫁衣襯著肌膚如雪吴汪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天蒸眠,我揣著相機與錄音漾橙,去河邊找鬼。 笑死楞卡,一個胖子當著我的面吹牛霜运,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蒋腮,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼淘捡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了池摧?” 一聲冷哼從身側(cè)響起焦除,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎险绘,沒想到半個月后踢京,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體誉碴,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年瓣距,在試婚紗的時候發(fā)現(xiàn)自己被綠了黔帕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹈丸,死狀恐怖成黄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逻杖,我是刑警寧澤奋岁,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站荸百,受9級特大地震影響闻伶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜够话,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一蓝翰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧女嘲,春花似錦畜份、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至愕鼓,卻和暖如春钙态,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拒啰。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工驯绎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谋旦。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓剩失,卻偏偏與公主長得像,于是被迫代替她去往敵國和親册着。 傳聞我的和親對象是個殘疾皇子拴孤,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容