JSONP 介紹

Posted by Noel on 2015-07-06

JSONP (JSON with Padding),聽起來跟JSON很像?兩者有什麼關連嗎?! JSONP 是一種跨網域資料交換的方式,而 JSON 則是一種資料交換的格式。而兩者的關聯就是 JSON 是AJAX在交換資料所常用的格式,而 JSONP 則是 AJAX 為突破同源政策 (Same-origin policy),而可讓不同網域之間一樣可以靠xhr取得資料的手段。


JSONP 由來:

在探討 JSONP 前一樣先來看看它是為何而生的呢?因為 AJAX 基於安全的考量,不允許對不同網域的網站發送請求,以防某些網站會在背後偷偷試圖連到其他網站去幹壞事(如:銀行網站之類的);但跨域的 AJAX 需求仍然常發生,因此當然有諸多因應之道,常見的有下:

  • 在同樣的網域下建立一支 proxy api 再利用這 proxy 去調用外部的資源
  • 伺服器端必須Access-Control-Allow-Origin設為*或給某些特定網域
  • 利用 script tag 的src載入

而 JSONP 主要則是利用其中的第三點來達成,這是因為 src 這個 attribute 不受是否跨網域的限制 (e.g. img, script, iframe),所以我們可以利用這個特性傳回一段可執行的 js code,例如

對於某個外部請求,server 會回傳一段 js code

1
alert('WTF')

而 clinet 這邊可以這樣來獲取

1
<script src="http://somewhere.com/test"></script>

結果就是會在頁面上跳出一句WTF,由此證明我們可以利用 src 不受跨網域限制的特性來克服以 往AJAX 所達不到的目的,而我們更進一步可以讓 server 端將希望回傳的資料(JSON)包成可執行的 js code 回傳給我們
如:

1
someFunction({ foo: 'bar', snsd: 'yoona'})

而我們只需要在 clinet 裡面先定義好someFunction這個 function 就可以發揮如同 callback 的效用達成跨網域的請求了!

JSONP 目的

所以,JSONP 主要就是讓 server 端將所有要回傳的資料包在任意名稱的 function 參數裡回傳給 client 端使用,JSON with Padding的名稱也非常貼切它的目的。而剛剛所提到任意名稱的 function,是因為我們既然要事先定義該 function,才可使用 server 回傳給我們的 js code,那必然也得讓 server 要傳什麼名稱給我們,簡單的說就是需要動態的產生 function 名,而這只需要把 function 的名稱當做參數傳過去即可,而後端的處理可以這樣寫:

some_controller.rb
1
2
3
4
def jsonp
@data = { name: 'Yoda', role: 'master' }.to_json
@func_name = params[:callback]
end
jsonp.js.erb
1
<%= @func_name %>(<%= raw @data %>)

client 的 code 會長這樣

1
2
3
4
5
6
7
	<script>
var say_hi = function(data){
alert('Hi '+ data.name)
}
</script>
<script src="http://somewhere.com/test?callback=say_hi">
</script>

JSONP 使用 jQuery

標準用法

實際上 JSONP 不算是官方正式提出用來解決跨網域需求存取的解法,算是非官方的協定。然而 JSONP 本身也非使用 xhr 物件來達到跨網域的請求,但 jQuery 還是支援 jsonp 而且用法相當簡單:

1
2
3
4
5
6
7
8
9
10
11
	function test(data){
//do_something
}

$.ajax({
url: 'http://somewhere.com/test',
type: 'get',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'test'
})

只要把dataType設為jsonp即可。而參數jsonp: 預設為 callback,所以是 callback=?,如果 jsonp 設為 foo 那就是 foo=? 。

jsonpCallback: 預設是由 jQeury 亂數產生的唯一值當做 function name,如果 jsonpCallback 設為 bar,那就是 callback=bar。 最後前端就必須要有定義 function bar 讓他可以執行。

建議好像是讓兩者都為預設就可以了,因為 cache 的問題,亂數產生的 function name 像是callback=XXXXX 就會每次都被視為新的 request 就比較不會有被 cache 住,而拿不到即時更新過資料的問題了。

精簡用法

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
url: 'http://somewhere.com/test',
type: 'get',
dataType: 'jsonp',
success: function(data) {
//do_something
},
error: function() {
console.log('fail)
}
})

jsonp 預設為 callback,所以可以不需要寫,jsonpCallback 也建議不用設定,使用預設的亂數值即可,而且聰明的 jQuery 會自動把回傳的 data 帶入 success 這個 callback 使用,也不需要額外在定義 function 去接了,非常方便!!

網路上看到使用$.getJSON網路上有可以這樣試,jQuery 似乎會自動去偵測 callback 這個 querystring (不過本人還沒測試過)

1
2
3
$.getJSON('http://somewhere.com/test?callback=?', function(data) {
//do_something
})

以上就是關於 jsonp 的學習心得與分享,如有錯誤或其他建議都歡迎指正~