什麼是跨域?
簡單的來說,出於安全方面的考慮,頁面中的JavaScript無法訪問其他服務器上的數據,即“同源策略”。而跨域就是通過某些手段來繞過同源策略限制,實現不同服務器之間通信的效果。
什麼是JSONP?
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式,JSONP 是 JSON with padding(填充式 JSON 或參數式 JSON)的簡寫。JSONP(JSON with Padding)則是JSON 的一種“使用模式”,通過這種模式可以實現數據的跨域獲取。JSONP 由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數。回調函數的名字一般是在請求中指定的。而數據就是傳入回調函數中的 JSON 數據。
JSONP跨域的基本原理
在同源策略下,在某個服務器下的頁面是無法獲取到該服務器以外的數據的,但img、iframe、script等標籤是個例外,這些標籤可以通過src屬性請求到其他服務器上的數據。利用script標籤的開放策略,我們可以實現跨域請求數據,當然,也需要服務端的配合。當我們正常地請求一個JSON數據的時候,服務端返回的是一串JSON類型的數據,而我們使用JSONP模式來請求數據的時候,服務端返回的是一段可執行的JavaScript代碼。
舉個例子,假如需要從服務器(http://www.a.com/user?id=123)獲取的數據如下:
- {"id": 123, "name" : 張三, "age": 17}
那麼,使用JSONP方式請求(http://www.a.com/user?id=123?callback=foo)的數據將會是如下:
- foo({"id": 123, "name" : 張三, "age": 17});
當然,如果服務端考慮得更加充分,返回的數據可能如下:
- try{foo({"id": 123, "name" : 張三, "age": 17});}catch(e){}
這時候我們只要定義一個foo()函數,並動態地創建一個script標籤,使其的src屬性為http://www.a.com/user?id=123?callback=foo:
便可以使用foo函數來調用返回的數據了。
JSONP跨域原理探秘
我們知道,使用 XMLHTTPRequest 對象發送HTTP請求時,會遇到 同源策略 問題,域不同請求會被瀏覽器攔截。
那麼是否有方法能繞過 XMLHTTPRequest 對象進行HTTP跨域請求呢?
換句話說,不使用 XMLHTTPRequest 對象是否可以發送跨域HTTP請求呢?
細心的你可能會發現,像諸如:
這種標籤是不會遇到"跨域"問題的,嚴格意義上講,這不是跨域,跨域是指在腳本代碼中向非同源域發送HTTP請求,這只是跨站資源請求。
那麼,我們是否可以利用跨站資源請求這一方式來實現跨域HTTP請求呢?
以標籤為例進行探索,先看一段代碼:
function handler(data) {
alert(data);
// our code here...
}
handler('success');
A JSONP demo.
這段代碼中,有2個JavaScript片斷,第1個片斷中定義了一個處理函數handler(),這個處理函數比較簡單,沒有對數據做任何處理,只是把它alert出來;第2個片斷調用了它,運行這個頁面瀏覽器會彈出"success"。
我們假設第2個JavaScript片斷存儲在別的地方,然後我們使用的方式把它引入進來,像這樣:
function handler(data) {
alert(data);
// our code here...
}
A JSONP demo.
service.a.com/script/1.js:
handler('success');
這種方法和把JavaScript代碼直接寫在頁面是等效的,但是,我們由此可以聯想到什麼?
我們是否可以事先在本頁面定義處理程序,服務端返回JS腳本,腳本的內容就是對處理程序的回調,服務返回的數據通過參數的形式傳回:
handler('服務返回的數據');
然後通過動態向當前頁面head節點添加節點的方式來“偽造”HTTP請求?
於是,可以編寫這樣一個簡單的測試用例:
先寫服務端,非常簡單的一個服務,返回字符串"Hello World",一般處理程序Service.ashx:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace JSONPDemo.Service
{
///
/// Service2 的摘要說明
///
public class Service2 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("handler('Hello World');");
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
再寫客戶端,一個簡單的靜態Web頁,index.html:
// 跨域發送HTTP請求,從服務端獲取字符串"Hello World"
function getHello() {
var>
>
document.querySelector("head").appendChild(script);
}
// 處理函數
function handler(data) {
alert(data);
// our code here...
}
![教你如何使用JSONP實現跨域](http://p2.ttnews.xyz/loading.gif)
測試成功!
在這個測試例子中,我們使用一般處理程序編寫了一個簡單的返回Hello World的服務,然後使用動態創建節點的方式實現了跨域HTTP請求。
由於-->
閱讀更多 程序員界的彭于晏 的文章