JS、JQuery和ExtJs的跨域處理


1.什么是跨域?
跨域,JavaScript出於安全方面的考慮,不允許跨域調用其他頁面的對象。簡單地理解就是因為JavaScript同源策略的限制,a.com 域名下的js無法操作b.com或是c.a.com域名下的對象。
同源策略,它是由Netscape提出的一個著名的安全策略。現在所有支持JavaScript 的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,端口相同。當一個瀏覽器的兩個tab頁中分別打開來 百度和谷歌的頁面當一個百度瀏覽器執行一個腳本的時候會檢查這個腳本是屬於哪個頁面的,即檢查是否同源,只有和百度同源的腳本才會被執行。
更詳細的說明可以看下表:

URL 說明 是否允許通信
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 允許
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不同文件夾 允許
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不同端口 不允許
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不同協議 不允許
http://www.a.com/a.js
http://70.32.92.74/b.js
域名和域名對應ip 不允許
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不同 不允許
http://www.a.com/a.js
http://a.com/b.js
同一域名,不同二級域名(同上) 不允許(cookie這種情況下也不允許訪問
http://www.cnblogs.com/a.js
http://www.a.com/b.js
不同域名 不允許

特別注意兩點:
(1).如果是協議和端口造成的跨域問題“前台”是無能為力的,
(2).在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解為“Domains, protocols and ports must match”。

2.跨域請求數據解決方案
(1).document.domain+iframe的設置
對於主域相同而子域不同的例子,可以通過設置document.domain的辦法來解決。
(2).動態創建Script
雖然瀏覽器默認禁止了跨域訪問,但並不禁止在頁面中引用其他域的JS文件,並可以自由執行引入的JS文件中的function(包括操作cookie、Dom等等)。
(3).利用iframe和location.hash
這個辦法比較繞,但是可以解決完全跨域情況下的腳步置換問題。原理是利用location.hash來進行傳值。
(4).Window.name實現的跨域數據傳輸
iframe的src屬性由外域轉向本地域,跨域數據即由iframe的window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操作。
(5).使用HTML5 postMessage
HTML5中最酷的新功能之一就是 跨文檔消息傳輸Cross Document Messaging。
(6).利用flash
上述,六種方式都可以處理JavaScript的跨域請求數據問題,詳細參見:Rain Man的《JavaScript跨域總結與解決辦法》。
除了上面六種方式,大家平時估計都在用腳本框架開發,在JQuery框架和ExtJs框架中處理JS跨域問題,常用JSONP來處理。

3.什么是JSONP?
JSONP(JSON with Padding)是一個非官方的協議,它允許在服務器端集成Script Tags返回至客戶端,通過Javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。
由於同源策略的限制,XMLHttpRequest只允許請求當前源(域名、協議、端口)的資源,為了實現跨域請求,可以通過script標簽實現跨域請求,然后在服務端輸出JSON數據並執行回調函數,從而解決了跨域的數據請求。

4..JSONP如何產生的?
(1).跨域訪問無權限。
眾所周知的問題,Ajax直接請求普通文件存在跨域無權限訪問的問題,不管是靜態頁面、動態網頁、web服務、WCF,只要是跨域請求,都無權限;
(2)."src"屬性標簽有跨域能力。
發現在Web頁面上調用js文件時則不受是否跨域的影響(不僅如此,擁有"src"屬性的標簽都擁有跨域能力,比如<script>、<img>、<iframe>);
(3).將數據裝進JS格式數據。
如果想通過純Web端(ActiveX控件、服務端代理、HTML5之Websocket等方式不算)跨域訪問數據就只有一種可能,就是在遠程服務器上設法把數據裝進js格式的數據里,供客戶端調用和進一步處理;
(4).JSON格式承載數據適合。
有一種JSON的純字符數據格式可以簡潔的描述復雜數據,更妙的是JSON還被JS原生支持,所以在客戶端幾乎可以隨心所欲的處理這種格式的數據;
(5).動態生成JSON格式數據。
Web客戶端可以通過與調用腳本一模一樣的方式,來調用跨域服務器上動態生成的js格式文件,顯而易見,服務器之所以要動態生成JSON文件,目的在於把客戶端需要的數據裝入進去。
(6).JSON數據成功回調到客戶端。
客戶端在對JSON文件調用成功之后,也就獲得了自己所需的數據,剩下的就是按照自己需求進行處理和展現了,這種獲取遠程數據的方式看起來非常像AJAX,但實質上是不一樣。
(7).形成一種非正式傳輸協議JSONP。
為了便於客戶端使用數據,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回的數據。

5.JSONP的工作原理
JSONP的原理:創建一個回調函數,動態創建Script標簽,然后在遠程服務上調用這個函數並且將JSON 數據形式作為參數傳遞,完成回調。將JSON數據填充進回調函數,進行相關的邏輯處理,或許這就是JSONP的JSON+Padding的含義。
(1).跨域簡單原理
新建一個asp.net的web程序,添加sample.html網頁和一個test.js文件,代碼如下:
sample.html的代碼:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head>
    <title>test</title>
    <script type="text/javascript" src="test.js"></script>
  </head>
  <body>
  </body>
</html>

test.js的代碼:

 alert("success");

打開sample.html后會跳出"success”這樣的這樣的信息框,這似乎並不能說明什么, 跨域問題到底怎么解決呢?
現在模擬非同源的環境,把上面的Web程序叫做A程序,再新建一個Web程序叫做B程序,將A程序的test.js文件移除然后拷貝到B程序中。將兩個程序都運行起來,Visual Studio會啟動內置服務器,假設A程序是localhost:20001,B程序是localhost:20002,這就模擬了一個非同源的環境了(雖然域名相同但端口號不同,所以是非同源的)。

現在改下A程序sample.html里的代碼,因為test.js文件在B程序上了,url也就變成了localhost:20002。

sample.html部分代碼:

<script type="text/javascript" src="http://localhost:20002/test.js"></script>

請保持AB兩個Web程序的運行狀態,當你再次刷新A程序localhost:20001/sample.html的時候,和原來一樣跳出了"success"的對話框,這樣就成功訪問到了非同源的B程序localhost:20002/test.js這個所謂的遠程服務了。到這里,大家應該已經大概明白如何跨域訪問的原理了。
<script>標簽的src屬性並不被同源策略所約束,所以可以獲取任何服務器上腳本並執行。

(2).跨域實現CallBack
繼續修改代碼,實現JSONP的JavaScript callback形式。
修改程序A中sample的代碼:

<script type="text/javascript">
  //回調函數
  function callback(data) {
    alert(data.message);
  }
</script>
<script type="text/javascript" src="http://localhost:20002/test.js"></script>

程序B中test.js的代碼:

//調用callback函數,並以json數據形式作為闡述傳遞,完成回調
callback({message:"success"});

這其實就是JSONP的簡單實現模式,或者說是JSONP的原型:創建一個回調函數,然后在遠程服務上調用這個函數並且將JSON 數據形式作為參數傳遞,完成回調。
(3).跨域實現動態JS腳本

怎么讓遠程js知道它應該調用的本地函數叫什么名字?畢竟是jsonp的服務者都要面對很多服務對象,而這些服務對象各自的本地函數都不相同。只要服務端提供的js腳本是動態生成的就可以,這樣調用者可以傳一個參數過去告訴服務端“我想要一段調用XXX函數的js代碼,請你返回給我”,於是服務器就可以按照客戶端的需求來動態生成js腳本並響應了。
程序A中sample的代碼:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript">
    // 得到航班信息查詢結果后的回調函數
    var flightHandler = function(data){
        alert('你查詢的航班結果是:票價 ' + data.price + ' 元,余票 ' + data.tickets + ' 張。');
    };
    // 提供jsonp服務的url地址(不管是什么類型的地址,最終生成的返回值都是一段javascript代碼)
    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
    // 創建script標簽,設置其屬性
    var script = document.createElement('script');
   script.setAttribute("type","text/javascript");
   script.setAttribute('src', url);
 // 把script標簽加入head,此時調用開始
 document.getElementsByTagName('head')[0].appendChild(script); </script>
</head>
<body>
</body>
</html>

這樣不直接把遠程js文件寫死,而是編碼實現動態查詢,而這也正是jsonp客戶端實現的核心部分,其重點也就在於如何完成jsonp調用的全過程。
看到調用的url中傳遞了一個code參數,告訴服務器我要查的是CA1998次航班的信息,而callback參數則告訴服務器,本地回調函數叫做flightHandler,所以請把查詢結果傳入這個函數中供本地調用。
程序B中test.js的代碼:

flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});

我們看到,傳遞給flightHandler函數的是一個json,它描述了航班的基本信息。運行一下頁面,成功彈出提示窗口,jsonp的執行全過程順利完成!

6.JQuery和ExtJs實現JSONP
(1).JQuery的JSONP跨域實現
<1>.$.getJSON
jQuery框架支持JSONP,可以使用$.getJSON(url,[data],[callback])方法(詳細可以參考http://api.jquery.com/jQuery.getJSON/)。繼續修改程序A的代碼,改用jQuery的getJSON方法來實現(下面的例子沒用用到向服務傳參,所以只寫了getJSON(url,[callback])):

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$.getJSON("http://localhost:20002/MyService.ashx?callback=?",function(data){
alert(data.name + " is a a" + data.sex);
});
</script>

要注意的是在url的后面必須添加一個callback參數,這樣getJSON方法才會知道是用JSONP方式去訪問服務,callback后面的那個問號是內部自動生成的一個回調函數名。這個函數名可以debug看一下,比如jQuery17207481773362960666_1332575486681。

<2>.$.ajax
假如說我們想指定自己的回調函數名,或者說服務上規定了固定回調函數名該怎么辦呢?可以使用$.ajax方法來實現(參數較多,詳細可以參見http://api.jquery.com/jQuery.ajax)。

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$.ajax({
    url:"http://localhost:20002/MyService.ashx?callback=?", 
    dataType:"jsonp",
    jsonpCallback:"person",
    success:function(data){
        alert(data.name + " is a a" + data.sex);
    }
});
</script>

jsonpCallback就是指定我們自己的回調方法名person,遠程服務接受callback參數的值就不再是自動生成的回調名,而是person。dataType是指定按照JSOPN方式訪問遠程服務。

(2).ExtJs的JSONP跨域實現
<1>.Ext.data.JsonP.request
ExtJS4.1的Ext.data.JsonP.request實現跨域訪問:

//跨域請求,MsgUrl為其他站點地址
Ext.data.JsonP.request({
    url: MsgUrl + '/Home/InitializeComet',
    timeout: 300000,
    params: { loginId: LoginId },
    callbackKey: "jsonPCallback",
    success: function(result) {
        if (result.rettype == 'true') {
            me.Comet.privateToken = result.msg;
            me.RegisterComet();
        } else {
            alert(result.msg);
        }
    },
    failure: function(result) {
        alert(result);
    }
});                

其中跨域請求的要點是類名:Ext.data.JsonP和callbackKey的參數。
“jsonPCallback”該名稱將作為Jsonp請求的方法名傳遞到服務器端,獲取該請求的URL:
http://10.0.13.64:89/Home/InitializeComet?loginId=0001&jsonPCallback=Ext.data.JsonP.callback1&_dc=1370687739484

<2>.Ext.data.ScriptTagProxy

var ss = new Ext.data.ScriptTagProxy({
  //url: 'http://10.128.3.104/edi/rest/GetBillCaseInfo',
    url: 'testjson.do',
    callbackParam: "_callback",
    headers: { 'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=' }
});
ss.load({ '_out': 'json' },
  new Ext.data.JsonReader(
    { root: "ROWSET.ROW" },
    [{ name: 'CaseCode', mapping: 'CaseCode' },{ name: 'CaseName', mapping: 'CaseName'}]),
    function (recordsBlock, arg, isok) {
        alert(Ext.encode(recordsBlock));
        alert(Ext.encode(recordsBlock.records[0].data));
}
);

 

Ext.Ajax.request({
    url: 'http://10.128.3.104/edi/rest/GetBillCaseInfo',
    //url: 'testjson.do',
    scriptTag: true,
    success: function (req) {
        alert(req.responseText);
    },
    failure: function (req) {
        alert(req.responseText);
    },
    headers: { 'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=' },
    params: { _out: 'json' }
});

7.AJAX與JSONP的異同
(1).Ajax和Jsonp是兩種技術。
Ajax和Jsonp這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個URL,然后把服務器返回的數據進行處理,因此JQuery和EXT等框架都把Jsonp作為Ajax的一種形式進行了封裝;
(2).Ajax和Jsonp實現原理不同。
Ajax和Jsonp在本質實現上有差別。Ajax的核心是通過XmlHttpRequest獲取非本頁內容,而Jsonp的核心則是動態添加<script>標簽來調用服務器提供的js腳本。所以說,其實Ajax與jsonp的區別不在於是否跨域,ajax通過服務端代理一樣可以實現跨域,Jsonp本身也不排斥同域的數據的獲取;
(3).Ajax和Jsonp都是非強制性協議。
Jsonp是一種方式或者說非強制性協議,如同Ajax一樣,它也不一定非要用Json格式來傳遞數據,如果你願意,字符串都行,只不過這樣不利於用Jsonp提供公開服務。
總而言之,Jsonp不是Ajax的一個特例,哪怕Jquery、Ext等巨頭把Jsonp封裝進了Ajax,也不能改變這一點!

參考博客:
Ajax與JSON的一些總結
JavaScript跨域總結與解決辦法
深入淺出JSONP--解決ajax跨域問題
說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
Jquery跨域請求
ExtJs學習筆記(23)-ScriptTagProxy+XTemplate+WCF跨域取數據
ExtJs與WCF之間的跨域訪問
jQuery與Extjs的Ajax的跨域訪問


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM