js跨域取數據功能的實現

ajax 跨域實現 前兩天xz問我知不知道ajax怎麼實現跨域調用,因為沒聽過這個概念,所以也知道怎麼實現。xz說ajax跨域調用有幾種方式,一種是iframe的方式,通過設置document.domain來實現,一種則是通過設置jsonp來實現。這兩天查瞭一下資料,也寫瞭幾個demo,下面備忘一下。
我在本地建瞭三個站點,並設置瞭host文件模擬跨子域和跨全域
coolkissbh.com
blog.coolkissbh.com
coolkiss.com
 
一 、ajax 跨域調用會有什麼問題
coolkissbh.com下頁面使用jquery的$.get調用blog.coolkissbh.com頁面
 
跨域請求,IE 7和8下報 access denied錯誤
IE 6.0 則彈出 this page is accessing information that is not under its control. this poses a security risk.do you want to continue?提示框
firefox 沒報錯,但是不會做出請求
 
二、ajax跨域實現方法

1、跨子域實現ajax

要求:實現coolkissbh.com的頁面 異步請求 blog.coolkissbh.com下的頁面
實現方法:借助iframe,通過設置iframe的src屬性,嵌入blog.coolkissbh.com下的一個頁面,比如AjaxProxy.aspx,然後由該頁面去請求Ajax
              AjaxProxy請求完畢後,通過parent對象把返回的數據回傳給coolkissbh.com的主頁面。因此,真正的異步請求還是發生
              在blog.coolkissbh.com的域名下
註意:通過這種方法實現的跨子域ajax請求,需要在coolkissbh.com的請求頁面以及AjaxProxy.aspx頁面中設置同樣的域名,也就是
        document.domain = "coolkissbh.com";
註意:關於設置domain的問題,如果是跨全域,使用上面方法時候,firefox下會提示
        Illegal document.domain value" code: "1009的錯誤,因此跨全域隻能使用第二種方法
處理返回的數據:
AjaxProxy.aspx將ajax返回的數據保存到一個全局變量中,coolkissbh.com通過setInterval定時去判斷iframe的頁面是否加載完成
如果加載完成,則獲取AjaxProxy.aspx的全局變量值。然後再做其它處理。
這裡有個問題:我原來是打算在AjaxProxy.aspx的ajax請求完成後,調用parent的方法,同時將數據返回,但是在IE下,點擊第一次時候
就會出現“permission denied”的錯誤,再次點擊就正常瞭。在firefox下就沒有問題,不知道是什麼原因。
2,跨全域實現ajax
要求:實現coolkissbh.com的頁面異步請求coolkiss.com下的頁面
實現方法:上面提到跨全域不能通過設置domain方法來實現。但是可以使用script標簽來實現,通過設置script標簽的src屬性為coolkiss.com域名
下的一個頁面,同時將callback函數傳到該頁面中,例如:
        RequestAjax_CrossSite = function() {
            var obj = $("#crossSitePage");
            obj.attr("src", "http://coolkiss.com/CrossSite.aspx?callback=handleData3");
        }
        handleData3 = function(data) {
            $("#ResponseData").html(data);
        }
CrossSite.aspx返回一個字符串,將返回的數據回傳給callback,執行回調函數,實現ajax,例如:
Response.Clear();
Response.Write(string.Format("{0}('{1}')", Request["callback"], responseData));
Response.End();
註意:這種方法同樣可以用於處理跨子域ajax的問題,但是就無法像jquery那樣獲取ajax調用的各個狀態

3,通過jquery的jsonp實現跨域ajax,其實原理跟第二種方法是一樣的,支持跨全域和子域
jquery 1.2 後添加瞭對跨域ajax的調用,也就是$.getJSON 函數
調用方法如下:
 下面是coolkissbh.com下的頁面

       //使用jsonp實現跨全域
RequestAjax_JSONP = function() {
     var obj = $("#crossSitePage");
     $.getJSON("http://coolkiss.com/CrossSite.aspx?callback=?&t=" + Math.random(), function(data) {
           //alert(data);
           $("#ResponseData").html(data.content);
        });
     }
 
coolkiss.com後臺處理代碼,將一個json對象傳遞給callback:
 
public partial class CrossSite : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            LoadData_JSONP();
        }
    }
    protected void LoadData_JSONP()
    {
        string responseData = "{content:/"hello world from coolkiss.com/"}";
        if (Request["callback"] != null && !string.IsNullOrEmpty(Request["callback"]))
        {
            Response.Clear();
            Response.Write(string.Format("{0}({1})", Request["callback"], responseData));
            Response.End();
        }
    }
}
 
 
callback=?  其中?會自動替換為function(data)函數。
———————————————————————————————————————————-
JavaScript是一種在Web開發中經常使用的前端動態腳本技術。在JavaScript中,有一個很重要的安全性限制,被稱為“Same-Origin Policy”(同源策略)。這一策略對於JavaScript代碼能夠訪問的頁面內容做瞭很重要的限制,即JavaScript隻能訪問與包含它的文檔在同一域下的內容。
JavaScript這個安全策略在進行多iframe或多窗口編程、以及Ajax編程時顯得尤為重要。根據這個策略,在baidu.com下的頁面中包含的JavaScript代碼,不能訪問在google.com域名下的頁面內容;甚至不同的子域名之間的頁面也不能通過JavaScript代碼互相訪問。對於Ajax的影響在於,通過XMLHttpRequest實現的Ajax請求,不能向不同的域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。
然而,當進行一些比較深入的前端編程的時候,不可避免地需要進行跨域操作,這時候“同源策略”就顯得過於苛刻。本文就這個問題,概括瞭跨域所需要的一些技術。
下面我們分兩種情況討論跨域技術:首先討論不同子域的跨域技術,然後討論完全不同域的跨域技術。
(一)不同子域的跨域技術。
我們分兩個問題來分別討論:第一個問題是如何跨不同子域進行JavaScript調用;第二個問題是如何向不同子域提交Ajax請求。
先來解決第一個問題,假設example.com域下有兩個不同子域:abc.example.com和def.example.com。現在假設在def.example.com下面有一個頁面,裡面定義瞭一個JavaScript函數:
function funcInDef() {
…..
}
我們想在abc.example.com下的某個頁面裡調用上面的函數。再假設我們要討論的abc.example.com下面的這個頁面是以iframe形式嵌入在def.example.com下面那個頁面裡的,這樣我們可能試圖在iframe裡做如下調用:
window.top.funcInDef();
好,我們註意到,這個調用是被前面講到的“同源策略”所禁止的,JavaScript引擎會直接拋出一個異常。
為瞭實現上述調用,我們可以通過修改兩個頁面的domain屬性的方法做到。例如,我們可以將上面在abc.example.com和def.example.com下的兩個頁面的頂端都加上如下的JavaScript代碼片段:
<script type="text/javascript">
document.domain = "example.com";
</script>
這樣,兩個頁面就變為同域瞭,前面的調用也可以正常執行瞭。
這裡需要註意的一點是,一個頁面的document.domain屬性隻能設置成一個更頂級的域名(除瞭一級域名),但不能設置成比當前域名更深層的子域名。例如,abc.example.com的頁面隻能將它的domain設置成example.com,不能設置成sub.abc.example.com,當然也不能設置成一級域名com。
上面的例子討論的是兩個頁面屬於iframe嵌套關系的情況,當兩個頁面是打開與被打開的關系時,原理也完全一樣。
下面我們來解決第二個問題:如何向不同子域提交Ajax請求。
通常情況下,我們會用與下面類似的代碼來創建一個XMLHttpRequest對象:
factories = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
];
function newRequest() {
for(var i = 0; i < factories.length; i++) {
try{
var factory = factories[i];
return factory();
} catch(e) {}
}
return null;
}
上面的代碼中引用ActiveXObject,是為瞭兼容IE6系列瀏覽器。每次我們調用newRequest函數,就獲得瞭一個剛剛創建的Ajax對象,然後用這個Ajax對象來發送HTTP請求。例如,下面的代碼向abc.example.com發送瞭一個GET請求:
var request = newRequest();
request.open("GET", "http://abc.example.com" );
request.send(null);
假設上面的代碼包含在一個abc.example.com域名下的頁面裡,則這個GET請求可以正常發送成功,沒有任何問題。然而,如果現在要向def.example.com發送請求,則出現跨域問題,JavaScript引擎拋出異常。
解決的辦法是,在def.example.com域下放置一個跨域文件,假設叫crossdomain.html;然後將前面的newRequest函數的定義移到這個跨域文件中;最後像之前修改document.domain值的做法一樣,在crossdomain.html文件和abc.example.com域下調用Ajax的頁面頂端,都加上:
<script type="text/javascript">
document.domain = "example.com";
</script>
為瞭使用跨域文件,我們在abc.example.com域下調用Ajax的頁面中嵌入一個隱藏的指向跨域文件的iframe,例如:
<iframe name="xd_iframe" style="display:none" src="http://def.example.com/crossdomain.html"></iframe>
這時abc.example.com域下的頁面和跨域文件crossdomain.html都在同一個域(example.com)下,我們可以在abc.example.com域下的頁面中去調用crossdomain.html中的newRequest函數:
var request = window.frames["xd_iframe"].newRequest();
這樣獲得的request對象,就可以向http://def.example.com發送HTTP請求瞭。
(二)完全不同域的跨域技術。
如果頂級域名都不相同,例如example1.com和example2.com之間想通過JavaScript在前端通信,則所需要的技術更復雜些。
在講解不同域的跨域技術之前,我們首先明確一點,下面要講的技術也同樣適用於前面跨不同子域的情況,因為跨不同子域隻是跨域問題的一個特例。當然,在恰當的情況下使用恰當的技術,能夠保證更優的效率和更高的穩定性。
簡言之,根據不同的跨域需求,跨域技術可以歸為下面幾類:
JSONP跨域GET請求
通過iframe實現跨域
flash跨域HTTP請求
window.postMessage
下面詳細介紹各種技術。
1. JSONP。
利用在頁面中創建<script>節點的方法向不同域提交HTTP請求的方法稱為JSONP,這項技術可以解決跨域提交Ajax請求的問題。JSONP的工作原理如下所述:
假設在http://example1.com/index.php這個頁面中向http://example2.com/getinfo.php提交GET請求,我們可以將下面的JavaScript代碼放在http://example1.com/index.php這個頁面中來實現:
var eleScript= document.createElement("script");
eleScript.type = "text/javascript";
eleScript.src = "http://example2.com/getinfo.php";
document.getElementsByTagName("HEAD")[0].appendChild(eleScript);
當GET請求從http://example2.com/getinfo.php返回時,可以返回一段JavaScript代碼,這段代碼會自動執行,可以用來負責調用http://example1.com/index.php頁面中的一個callback函數。
JSONP的優點是:它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;並且在請求完畢後可以通過調用callback的方式回傳結果。
JSONP的缺點則是:它隻支持GET請求而不支持POST等其它類型的HTTP請求;它隻支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。
2. 通過iframe實現跨域。
iframe跨域的方式,功能強於JSONP,它不僅能用來跨域完成HTTP請求,還能在前端跨域實現JavaScript調用。因此,完全不同域的跨域問題,通常采用iframe的方式來解決。
與JSONP技術通過創建<script>節點向不同的域提交GET請求的工作方式類似,我們也可以通過在http://example1.com/index.php頁面中創建指向http://example2.com/getinfo.php的iframe節點跨域提交GET請求。然而,請求返回的結果無法回調http://example1.com/index.php頁面中的callback函數,因為受到“同源策略”的影響。
為瞭解決這個問題,我們需要在example1.com下放置一個跨域文件,比如路徑是http://example1.com/crossdomain.html。
當http://example2.com/getinfo.php這個請求返回結果的時候,它大體上有兩個選擇。
第一個選擇是,它可以在iframe中做一個302跳轉,跳轉到跨域文件http://example1.com/crossdomain.html,同時將返回結果經過URL編碼之後作為參數綴在跨域文件URL後面,例如http://example1.com/crossdomain.html?result=<URL-Encoding-Content>。
另一個選擇是,它可以在返回的頁面中再嵌入一個iframe,指向跨域文件,同時也是將返回結果經過URL編碼之後作為參數綴在跨域文件URL後面。
在跨域文件中,包含一段JavaScript代碼,這段代碼完成的功能,是從URL中提取結果參數,經過一定處理後調用原來的http://example1.com/index.php頁面中的一個預先約定好的callback函數,同時將結果參數傳給這個函數。http://example1.com/index.php頁面和跨域文件是在同一個域下的,因此這個函數調用可以通過。跨域文件所在iframe和原來的http://example1.com/index.php頁面的關系,在前述第一種選擇下,後者是前者的父窗口,在第二種選擇下,後者是前者的父窗口的父窗口。
根據前面的敘述,有瞭跨域文件之後,我們就可以實現通過iframe方式在不同域之間進行JavaScript調用。這個調用過程可以完全跟HTTP請求無關,例如有些站點可以支持動態地調整在頁面中嵌入的第三方iframe的高度,這其實是通過在第三方iframe裡面檢測自己頁面的高度變化,然後通過跨域方式的函數調用將這個變化告知父窗口來完成的。
既然利用iframe可以實現跨域JavaScript調用,那麼跨域提交POST請求等其它類型的HTTP請求就不是難事。例如我們可以跨域調用目標域的JavaScript代碼在目標域下提交Ajax請求(GET/POST/etc.),然後將返回的結果再跨域傳原來的域。
使用iframe跨域,優點是功能強大,支持各種瀏覽器,幾乎可以完成任何跨域想做的事情;缺點是實現復雜,要處理很多瀏覽器兼容問題,並且傳輸的數據不宜過大,過大瞭可能會超過瀏覽器對URL長度的限制,要考慮對數據進行分段傳輸等。
3. 利用flash實現跨域HTTP請求
據稱,flash在瀏覽器中的普及率高達90%以上。
flash代碼和JavaScript代碼之間可以互相調用,並且flash的“安全沙箱”機制與JavaScript的安全機制並不盡相同,因此,我們可以利用flash來實現跨域提交HTTP請求(支持GET/POST等)。
例如,我們用瀏覽器訪問http://example1.com/index.php這個頁面,在這個頁面中引用瞭http://example2.com/flash.swf這個flash文件,然後在flash代碼中向http://example3.com/webservice.php發送HTTP請求。
這個請求能否被成功發送,取決於在example3.com的根路徑下是否放置瞭一個crossdomain.xml以及這個crossdomain.xml的配置如何。flash的“安全沙箱”會保證:僅當example3.com服務器在根路徑下確實放置瞭crossdomain.xml文件並且在這個文件中配置瞭允許接受來自example2.com的flash的請求時,這個請求才能真正成功。下面是一個crossdomain.xml文件內容的例子:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="example2.com" />
</cross-domain-policy>
4. window.postMessage
window.postMessage是HTML標準的下一個版本HTML5支持的一個新特性。受當前互聯網技術突飛猛進的影響,瀏覽器跨域通信的需求越來越強烈,HTML標準終於把跨域通信考慮進去瞭。但目前HTML5仍然隻是一個draft。
window.postMessage是一個安全的實現直接跨域通信的方法。但是目前並不是所有瀏覽器都能支持,隻有Firefox 3、Safari 4和IE8可以支持這個調用。
使用它向其它窗口發送消息的調用方式大概如下:
otherWindow.postMessage(message, targetOrigin);
在接收的窗口,需要設置一個事件處理函數來接收發過來的消息:
window.addEventListener("message", receiveMessage, false);function receiveMessage(event){ if (event.origin!== "http://example.org:8080") return;}消息包含三個屬性:data、origin(攜帶發送窗口所在域的真實信息)和source(代表發送窗口的handle)。
安全性考慮:使用window.postMessage,必需要使用消息的origin和source屬性來驗證發送者的身份,否則會造成XSS漏洞。
window.postMessage在功能上同iframe實現的跨域功能同樣強大,並且使用簡單,效率更高,但缺點是它目前在瀏覽器兼容方面有待提高。

總結完所有的跨域方式之後,我們要時刻銘記,雖然跨域技術能給你帶來更多的功能,催生出更靈活和更加平臺化的產品,但是功能的放開也總是意味著安全的風險。在實現跨域技術的每個步驟和細節,都要時刻在頭腦中考慮到對安全帶來的影響,避免成為XSS攻擊的漏洞。
————————————————————————————————————————————————————————-
javascript出於安全方面的考慮,是不允許跨域調用其他頁面的對象的。但在安全限制的同時也給註入iframe或是ajax應用上帶來瞭不少麻煩。沒有記錯的話前三屆D2論壇上每次都有人提這個東西,這裡把涉及到跨域的一些問題簡單地整理一下:
首先什麼是跨域,簡單地理解就是因為javascript同源策略的限制,a.com 域名下的js無法操作b.com或是c.a.com域名下的對象。更詳細的說明可以看下表:
URL 說明 是否允許通信
http://www.f2e.me/lab/a.js
http://www.f2e.me/script/b.js 同一域名下不同文件夾 允許
http://www.f2e.me/a.js
http://www.f2e.me/b.js 同一域名下 允許
http://www.f2e.me:8000/a.js
http://www.f2e.me/b.js 同一域名,不同端口 不允許
http://www.f2e.me/a.js
https://www.f2e.me/b.js 同一域名,不同協議 不允許
http://www.f2e.me/a.js
http://70.32.92.74/b.js 域名和域名對應ip 不允許
http://www.f2e.me/a.js
http://script.f2e.me/b.js 主域相同,子域不同 不允許
http://www.space007.com/a.js
http://www.f2e.me/b.js 不同域名 不允許
特別註意兩點:
第一,如果是協議和端口造成的跨域問題“前臺”是無能為力的,
第二:在跨域問題上,域僅僅是通過URL的首部來識別而不會去嘗試判斷相同的ip地址對應著兩個域或兩個域是否在同一個ip上。
接下來簡單地總結一下在“前臺”一般處理跨域的辦法,後臺proxy這種方案牽涉到後臺的配置,這裡就不闡述瞭,有興趣的可以看看YAHOO的這篇文章: ?
JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls。
1、document.domain+iframe的設置
對於主域相同而子域不同的例子,可以通過設置document.domain的辦法來解決。具體的做法是可以在http://www.f2e.me/a.html和http://script.f2e.me/b.html兩個文件中分別加上document.domain = ‘f2e.me’;然後通過a.html文件中創建一個iframe,去控制iframe的contentDocument,這樣兩個js文件之間就可以“交互”瞭。當然這種辦法隻能解決主域相同而二級域名不同的情況,如果你異想天開的把script.f2e.me的domian設為alibaba.com那顯然是會報錯地!代碼如下:
www.f2e.me上的a.html
        document.domain = 'f2e.me';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.f2e.me/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var x = ifr.contentDocument;
alert(x.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
}
script.f2e.me上的b.html
document.domain = 'f2e.me';
2、動態創建script
雖然瀏覽器默認禁止瞭跨域訪問,但並不禁止在頁面中引用其他域的JS文件,並可以自由執行引入的JS文件中的function,根據這一點,可以方便地通過創建script節點的方法來實現完全跨域的通信。具體的做法可以參考yui的 Get Utility
這裡判斷script節點加載完畢還是蠻有意思的:ie隻能通過script的readystatechange屬性,Safari 3.x以上支持的是script的load事件,而firefox和oprea則要通過onload來解決。另外這種辦法隻能傳遞js類型的數據,不是很方便。以下是部分判斷script加載完畢的方法。
……
// ie支持script的readystatechange屬性
// IE supports the readystatechange event for script and css nodes
if (ua.ie) {
n.onreadystatechange = function() {
var rs = this.readyState;
if ("loaded" === rs || "complete" === rs) {
n.onreadystatechange = null;
f(id, url);
}
};
……
// // Safari 3.x supports the load event for script nodes (DOM2)
……
n.addEventListener("load", function() {
f(id, url);
});
……
// FireFox and Opera support onload (but not DOM2 in FF) handlers for
// script nodes.  Opera, but not FF, supports the onload event for link
// nodes.
} else {
n.onload = function() {
f(id, url);
};
}
3、利用iframe和location.hash
這個辦法比較繞,但是可以解決完全跨域情況下的腳步置換問題。原理是利用location.hash來進行傳值。在url:http://f2e.me#helloword中的‘#helloworld’就是location.hash,改變hash並不會導致頁面刷新,所以可以利用hash值來進行數據傳遞,當然數據容量是有限的。假設域名f2e.me下的文件cs1.html要和space007.com域名下的cs2.html傳遞信息,cs1.html首先創建自動創建一個隱藏的iframe,iframe的src指向space007.com域名下的cs2.html頁面,這時的hash值可以做參數傳遞用。cs2.html響應請求後再將通過修改cs1.html的hash值來傳遞數據。(因為ie不允許修改parent.location.hash的值,所以要借助於f2e.me域名下的一個代理iframe)。同時在cs1.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一點有變化則獲取獲取hash值。代碼如下:
先是f2e.me下的文件cs1.html文件:
function startRequest(){
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://www.space007.com/lab/cscript/cs2.html#paramdo';
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1):'';
if(console.log){
console.log('Now the data is '+data);
}
}catch(e){};
}
setInterval(checkHash, 2000);
space007.com域名下的cs2.html:
(function(){
//模擬一個簡單的參數處理操作
switch(location.hash){
case '#paramdo':
callBack();
break;
case '#paramset':
//do something……
break;
}
function callBack(){
try {
            parent.location.hash = 'somedata';
        } catch (e) {
        //ie的安全機制無法修改parent.location.hash,所以要利用一個中間在space007域下的代理iframe
        var ifrproxy = document.createElement('iframe');
        ifrproxy.style.display = 'none';
        ifrproxy.src = 'http://f2e.me/test/cscript/cs3.html#somedata';
        document.body.appendChild(ifrproxy);
        }
}
})();
f2e.me下的域名cs3.html
//因為parent.parent和自身屬於同一個域,所以ie下可以改變其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
實例請點擊  hash實現完全跨域
當然這樣做也存在很多缺點,諸如數據直接暴露在瞭url中,數據容量和類型都有限等……
4、利用flash
這是從YUI3的IO組件中看到的辦法,具體可見:http://developer.yahoo.com/yui/3/io/
flash這個方案不是很明白,各位自己慢慢琢磨瞭,呵呵。你可以看在Adobe Developer Connection看到更多的跨域代理文件規范:
ross-Domain Policy File Specifications.
HTTP Headers Blacklist.
———————————————————————————————————————————————-
跨域的安全限制都是指瀏覽器端來說的.服務器端是不存在跨域安全限制的,
所以通過本機服務器端通過類似httpclient方式完成“跨域訪問”的工作,然後在瀏覽器端用AJAX獲取本機服務器端“跨域訪問”對應的url.來間接完成跨域訪問也是可以的.但很顯然開發量比較大,但限制也最少,很多widget開放平臺server端(如sohu博客開放平臺)其實就麼搞的.不在本次討論范圍.
        要討論的是瀏覽器端的真正跨域訪問,推薦的是目前jQuery $.ajax()支持get方式的跨域,這其實是采用jsonp的方式來完成的.
真實案例:
var qsData = {'searchWord':$("#searchWord").attr("value"),'currentUserId':$("#currentUserId").attr("value"),'conditionBean.pageSize':$("#pageSize").attr("value")};
$.ajax({
   async:false,
   url: http://跨域的dns/document!searchJSONResult.action,
   type: "GET",
   dataType: 'jsonp',
   jsonp: 'jsoncallback',
   data: qsData,
   timeout: 5000,
   beforeSend: function(){
   //jsonp 方式此方法不被觸發.原因可能是dataType如果指定為jsonp的話,就已經不是ajax事件瞭
   },
   success: function (json) {//客戶端jquery預先定義好的callback函數,成功獲取跨域服務器上的json數據後,會動態執行這個callback函數
    if(json.actionErrors.length!=0){
           alert(json.actionErrors);
     }
       genDynamicContent(qsData,type,json);
   },
    complete: function(XMLHttpRequest, textStatus){
    $.unblockUI({ fadeOut: 10 });
   },
   error: function(xhr){
    //jsonp 方式此方法不被觸發.原因可能是dataType如果指定為jsonp的話,就已經不是ajax事件瞭
    //請求出錯處理
    alert("請求出錯(請檢查相關度網絡狀況.)");
   }
});
註意:$.getJSON(" http://跨域的dns/document!searchJSONResult.action?name1="+value1+"&jsoncallback=?",
      function(json){
      if(json.屬性名==值){
      // 執行代碼
            }
        });
這種方式其實是上例$.ajax({..}) api的一種高級封裝,有些$.ajax api底層的參數就被封裝而不可見瞭.
這樣,jquery就會拼裝成如下的url get請求
http://跨域的dns/document!searchJSONResult.action?&jsoncallback=jsonp1236827957501&_=1236828192549&searchWord=%E7%94%A8%E4%BE%8B&currentUserId=5351&conditionBean.pageSize=15
在響應端(http://跨域的dns/document!searchJSONResult.action),
通過 jsoncallback = request.getParameter("jsoncallback") 得到jquery端隨後要回調的js function name:jsonp1236827957501
然後 response的內容為一個Script Tags:"jsonp1236827957501("+按請求參數生成的json數組+")";
jquery就會通過回調方法動態加載調用這個js tag:jsonp1236827957501(json數組);
這樣就達到瞭跨域數據交換的目的.
    jsonp的最基本的原理是:動態添加一個<script>標簽,而script標簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關瞭.
這樣其實"jQuery AJAX跨域問題"就成瞭個偽命題瞭,jquery $.ajax方法名有誤導人之嫌.
如果設為dataType: 'jsonp', 這個$.ajax方法就和ajax XmlHttpRequest沒什麼關系瞭,取而代之的則是JSONP協議.
JSONP是一個非官方的協議,它允許在服務器端集成Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問
JSONP即JSON with Padding。由於同源策略的限制,XmlHttpRequest隻允許請求當前源(域名、協議、端口)的資源。如果要進行跨域請求,
我們可以通過使用html的script標記來進行跨域請求,並在響應中返回要執行的script代碼,其中可以直接使用JSON傳遞javascript對象。
這種跨域的通訊方式稱為JSONP。
jsonCallback 函數jsonp1236827957501(….): 是瀏覽器客戶端註冊的,獲取跨域服務器上的json數據後,回調的函數
Jsonp原理:
首先在客戶端註冊一個callback (如:'jsoncallback'), 然後把callback的名字(如:jsonp1236827957501)傳給服務器。註意:服務端得到callback的數值後,要用jsonp1236827957501(……)把將要輸出的json內容包括起來,此時,服務器生成 json 數據才能被客戶端正確接收。
然後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 'jsoncallback'的值 jsonp1236827957501 .
最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成瞭一段 js 語法的文檔,返回給客戶端。
客戶端瀏覽器,解析script標簽,並執行返回的 javascript 文檔,此時javascript文檔數據,作為參數,
傳入到瞭客戶端預先定義好的 callback 函數(如上例中jquery $.ajax()方法封裝的的success: function (json))裡.(動態執行回調函數)
     可以說jsonp的方式原理上和<script src="http://跨域/…xx.js"></script>是一致的(qq空間就是大量采用這種方式來實現跨域數據交換的) .JSONP是一種腳本註入(Script Injection)行為,所以也有一定的安全隱患.
    註意,jquey是不支持post方式跨域的.
    為什麼呢?
    雖然采用post +動態生成iframe是可以達到post跨域的目的(有位js牛人就是這樣把jquery1.2.5 打patch的),但這樣做是一個比較極端的方式,不建議采用.
    也可以說get方式的跨域是合法的,post方式從安全角度上,被認為是不合法的, 萬不得已還是不要劍走偏鋒..
    client端跨域訪問的需求看來也引起w3c的註意瞭,看資料說html5 WebSocket標準支持跨域的數據交換,應該也是一個將來可選的跨域數據交換的解決方案,
———————————————————————————————————————————————————————-
Ajax的應用中,由於安全的問題,瀏覽器默認是不支持跨域調用的。傳統解決的方法,包括:(參考http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/)
Local proxy:
Needs infrastructure (can't run a serverless client) and you get double-taxed on bandwidth and latency (remote – proxy – client).
Flash:
Remote host needs to deploy a crossdomain.xml file, Flash is relatively proprietary and opaque to use, requires learning a one-off moving target programming langage.
Script tag:
Difficult to know when the content is available, no standard methodology, can be considered a "security risk".
以上方法都各有缺陷,都不是很好多解決方案。後來出現瞭一種叫JSON with Padding 的技術,簡稱 JSONP .(原理參考http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/),應用JSONP可以實現JSON數據的跨域調用。非常的幸運,JQuery1.2以後支持JSONP的應用。下面側重說明在JQuery中,Json的跨域調用。
      應用JSONP實現Json數據跨域調用,需要服務器端與客戶端的合作完成。引用Jquery官方的例子,客戶端掉用如下:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
        function(data){
          $.each(data.items, function(i,item){
            $("<img/>").attr("src", item.media.m).appendTo("#images");
            if ( i == 3 ) return false;
          });
        });
 
    註意這裡調用的地址中jsoncallback=?是關鍵的所在!其中,符號會被Query自動替換成其他的回調方法的名稱,具體過程和原理我們這裡不理會。我們關心的是jsoncallback=?起什麼作用瞭?原來jsoncallback=?被替換後,會把方法名稱傳給服務器。我們在服務器端要做什麼工作呢?服務器要接受參數jsoncallback,然後把jsoncallback的值作為JSON數據方法名稱返回,比如服務器是JSP,我們會這樣做:
 
      …
      String jsoncallback=request.getParameter("jsoncallback");
      …
      out.print(jsoncallback+"({/"account/":/"XX/",/"passed/":/"true/",/"error/":/"null/"})");
 
Jquery取得的數據可能如下:
      JQUET0988788({"account":"XX","passed":"true","error":"null"})
 
總結,用JSONP要做兩件事:
      1/請求地址加參數:jsoncallback=?
      2/服務器段把jsoncallback的值作為方法名傳回來,如JQUET098788(…)

摘自 andy1219111的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *