JSONP的一點筆記

yint發表於2017-06-19

同源策略

同源策略(Same origin policy),它是由Netscape提出的一個著名的安全策略。現在所有支援JavaScript的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,埠相同。[2] 同源策略限制從一個源載入的文件或指令碼如何與來自另一個源的資源進行互動。這是一個用於隔離潛在惡意檔案的關鍵的安全機制。[3]

如果非同源,共有三種行為受到限制:

  1. Cookie、LocalStorage 和 IndexDB 無法讀取。
  2. DOM 無法獲得。
  3. AJAX請求不能傳送。

AJAX

Asynchronous JavaScript and XML (Ajax) Ajax 允許在不干擾 Web 應用程式的顯示和行為的情況下在後臺進行資料檢索。使用 XMLHttpRequest 函式獲取資料,它是一種 API,允許客戶端 JavaScript 通過 HTTP 連線到遠端伺服器。
對於AJAX以何種格式來交換資料、跨域需求如何解決。一種方案是:用JSON來傳資料,靠JSONP來跨域。


JSONP

是JSON with Padding的略稱。它是一個非官方的跨域資料互動協議協議,它允許在伺服器端整合Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。[4]

JSONP是怎麼產生的:

通俗的闡釋:

1.Ajax直接請求普通檔案存在跨域無許可權訪問的問題,無論是靜態頁面、動態網頁、web服務、WCF,只要是跨域請求,一律不準;

2.Web頁面上呼叫js檔案時則不受是否跨域的影響(凡是擁有”src”這個屬性的標籤都擁有跨域的能力,比如<script>、<img>、<iframe>);

3.基於上述,如果想通過純web端(ActiveX控制元件、服務端代理、HTML5之Websocket等方式暫不考慮)跨域訪問資料存在這樣一種可能:即在遠端伺服器上設法把資料裝進js格式的檔案裡,供客戶端呼叫和進一步處理;

4.恰巧,JSON作為純字元資料格式可以簡潔的描述複雜資料,更妙的是JSON還被js原生支援,所以在客戶端幾乎可以隨心所欲的處理這種格式的資料;

5.於是,解決方案:web客戶端通過與呼叫指令碼一模一樣的方式,來呼叫跨域伺服器上動態生成的js格式檔案(一般以JSON為字尾),顯而易見,伺服器之所以要動態生成JSON檔案,目的就在於把客戶端需要的資料裝入進去。

6.客戶端在對JSON檔案呼叫成功之後,也就獲得了自己所需的資料,剩下的就是按照自己需求進行處理和展現了,這種獲取遠端資料的方式看起來非常像AJAX,但其實並不一樣。

7.為了便於客戶端使用資料,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許使用者傳遞一個callback引數給服務端,然後服務端返回資料時會將這個callback引數作為函式名來包裹住JSON資料,這樣客戶端就可以隨意定製自己的函式來自動處理返回資料了。[6]


JSONP是伺服器與客戶端跨源通訊的常用方法。最大特點就是簡單適用,老式瀏覽器全部支援,伺服器改造非常小。

在“跨域”的問題上,我們發現src 屬性並沒有受到相關的限制,比如 img / script 等。

JSONP的基本思想是,網頁通過新增一個<script>標籤,設定這個script標籤的src屬性用於向伺服器請求JSON資料 ,src屬性的查詢字串一定要加一個callback函式,用來指定回撥函式的名字 。而這個函式是在資源載入之前就已經在前端定義好的,這個函式接受一個引數並利用這個引數做一些事情。向伺服器請求後,伺服器會將JSON資料放在一個指定名字的回撥函式裡作為其引數傳回來。這時,因為函式已經在前端定義好了,所以會直接呼叫。

首先,網頁動態插入<script>元素,由它向跨源網址發出請求。

function addScriptTag(src) {
  var script = document.createElement(`script`);
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag(`http://example.com/ip?callback=foo`);//請求伺服器資料並規定回撥函式為foo
}

function foo(data) {
  console.log(`Your public IP address is: ` + data.ip);
};

上面程式碼通過動態新增<script>元素,向伺服器example.com發出請求。注意,該請求的查詢字串有一個callback引數,用來指定回撥函式的名字,這對於JSONP是必需的。
伺服器收到這個請求以後,會將資料放在回撥函式的引數位置返回。

foo({
  "ip": "8.8.8.8"
});

由於<script>元素請求的指令碼,直接作為程式碼執行。這時,只要瀏覽器定義了foo函式,該函式就會立即呼叫。作為引數的JSON資料被視為JavaScript物件,而不是字串,因此避免了使用JSON.parse的步驟。[1]

jQuery對JSONP的實現

jQuery 擁有對 JSONP 回撥的本地支援。如果指定了 JSONP 回撥,就可以載入位於另一個域的 JSON 資料,回撥的語法為:url?callback=?。
jQuery 自動將 ? 替換為要呼叫的生成函式名。清單 4 顯示了該程式碼。
清單 4. 使用 JSONP 回撥

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

為此,jQuery 將一個全域性函式附加到插入指令碼時需要呼叫的視窗物件。另外,jQuery 也能優化非跨域呼叫。如果向同一個域發出請求,jQuery 就將其轉化為普通 Ajax 請求。[5]
jQuery框架也當然支援JSONP,可以使用$.getJSON(url,[data],[callback])方法。
http://api.jquery.com/jQuery….
要注意的是在url的後面必須新增一個callback引數,這樣getJSON方法才會知道是用JSONP方式去訪問服務,callback後面的那個問號是內部自動生成的一個回撥函式名。

使用 JSONP 支援的示例服務

在上一個例子中,使用了靜態檔案(ticker.js)將 JavaScript 動態插入到 Web 頁面中。儘管返回了 JSONP 回覆,但它不允許您在 URL 中定義回撥函式名。這不是 JSONP 服務。因此,如何才能將其轉換為真正的 JSONP 服務呢?可使用的方法很多。這裡我們將分別使用 PHP 和 Java 展示兩個示例。
首先,假設您的服務在所請求的 URL 中接受了一個名為 callback 的引數。(引數名不重要,但是客戶和伺服器必須都同意該名稱)。另外假設向服務傳送的請求是這樣的:

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

在這種情況下,symbol 是表示請求 ticker symbol 的請求引數,而 callback 是 Web 應用程式的回撥函式的名稱。使用清單 5 所示的程式碼可以通過 jQuery 的 JSONP 支援呼叫該服務。
清單 5. 呼叫回撥服務

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

注意,我們使用 ? 作為回撥函式名,而非真實的函式名。因為 jQuery 會用生成的函式名替換 ?。所以您不用定義類似於 showPrice() 的函式。

現成的 JSONP 服務

Digg API:來自 Digg 的頭條新聞:

http://services.digg.com/stories/top?appkey=http%3A%2F%2Fmashup.com&type=javascript
&callback=?

Geonames API:郵編的位置資訊:

http://www.geonames.org/postalCodeLookupJSON?postalcode=10504&country=US&callback=?

Flickr API:來自 Flickr 的最新貓圖片:

http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any
&format=json&jsoncallback=?

使用$.ajax方法來實現

http://api.jquery.com/jQuery….

CORS

(Cross-Origin Resource Sharing )
跨來源資源共享(CORS)是一份瀏覽器技術的規範,提供了 Web 服務從不同網域傳來沙盒指令碼的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 只能發GET要求不同,CORS 允許任何型別請求。用 CORS 可以讓網頁設計師用一般的 XMLHttpRequest,這種方式的錯誤處理比 JSONP 要來的好。另一方面,JSONP 可以在不支援 CORS 的老舊瀏覽器上運作。現代的瀏覽器都支援 CORS。[4]

Nginx反向代理跨域

用nginx的反向代理機制解決前端跨域問題

索引:

1.瀏覽器同源政策及其規避方法–阮一峰
2.baidu
3.MDN
4.wiki
5.結合 JSONP 和 jQuery 快速構建強大的 mashup
6.說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
7.W3Cschool

參見:

1.Web開發中跨域的幾種解決方案
2.瀏覽器的同源策略MDN
3.瀏覽器的同源策略
4.用nginx的反向代理機制解決前端跨域問題

相關文章