前端跨域問題相關知識詳解(原生js和jquery兩種方法實現jsonp跨域)


1、同源策略

同源策略(Same origin policy),它是由Netscape提出的一個著名的安全策略。同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現,現在所有支持JavaScript 的瀏覽器都會使用這個策略。

所謂同源,就是指兩個頁面具有相同的協議,主機(也常說域名),端口,三個要素缺一不可。

所謂同源策略,指的是瀏覽器對不同源的腳本或者文本的訪問方式進行的限制。即a.com 域名下的js無法操作b.com或是c.com域名下的對象。詳細見下表:

URL1 URL2 說明 是否允許通信
http://www.foo.com/js/a.js http://www.foo.com/js/b.js 協議、域名、端口都相同 允許
http://www.foo.com/js/a.js http://www.foo.com:8888/js/b.js 協議、域名相同,端口不同 不允許
https://www.foo.com/js/a.js http://www.foo.com/js/b.js 主機、域名相同,協議不同 不允許
http://www.foo.com/js/a.js http://www.bar.com/js/b.js 協議、端口相同,域名不同 不允許
http://www.foo.com/js/a.js http://foo.com/js/b.js 協議、端口相同,主域名相同,子域名不同 不允許

 

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
不同域名 不允許
eg:當瀏覽器的百度tab頁執行一個腳本的時候會檢查這個腳本是屬於哪個頁面的,即檢查是否同源,只有和百度同源的腳本才會被執行。如果非同源,那么在請求數據時,瀏覽器會在控制台中報一個異常,提示拒絕訪問。
特別注意兩點:
第一,如果是協議和端口造成的跨域問題“前台”是無能為力的;
第二:在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解為“Domains, protocols and ports must match”。

    前端所說的跨域一般指“前台”處理跨域的辦法,后台proxy這種方案牽涉到后台配置,有興趣的可以參考yahoo的這篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls

同源策略限制了不同源之間的交互,可我們平時文件中引用其他域名的js文件,css文件,圖片文件為何沒受到限制呢?同源策略限制的不同源之間的交互主要針對的是js中的XMLHttpRequest等請求,下面這些情況是完全不受同源策略限制的:

  • 頁面中的鏈接,重定向以及表單提交是不會受到同源策略限制的。鏈接就不用說了,導航網站上的鏈接都是鏈接到其他站點的。而你在域名www.foo.com下面提交一個表單到www.bar.com是完全可以的。
  • 跨域資源嵌入是允許的,當然,瀏覽器限制了Javascript不能讀寫加載的內容。如前面提到的嵌入的<script src="..."></script>,<img>,<link>,<iframe>等。當然,如果要阻止iframe嵌入我們網站的資源(頁面或者js等),我們可以在web服務器加上一個X-Frame-Options DENY頭部來限制。nginx就可以這樣設置add_header X-Frame-Options DENY;

互聯網的許多網站之間圖片相互盜鏈,A網站網頁的img.src直接鏈接到B網站的圖片地址,就是因為這個原因:<img>的src(獲取圖片),<link>的href(獲取css),<script>的src(獲取javascript)這三個都不符合同源策略,它們可以跨域獲取數據。因此,你可以直接從一些cdn上獲取jQuery,並且你網站上的圖片也隨時可能被別人盜用。

而我們的第一種跨域方法jsonp,就是因為<script>的src不符合同源策略而來的。

2、解決跨域問題-JSONP 

Asynchronous JavaScript and XML (Ajax ) 是驅動新一代 Web 站點(流行術語為 Web 2.0 站點)的關鍵技術。Ajax 允許在不干擾 Web 應用程序的顯示和行為的情況下在后台進行數據檢索。使用 XMLHttpRequest 函數獲取數據,它是一種 API,允許客戶端 JavaScript 通過 HTTP 連接到遠程服務器。Ajax 也是許多 mashup 的驅動力,它可將來自多個地方的內容集成為單一Web 應用程序。

不過,由於受到瀏覽器的限制,該方法不允許跨域通信。

即:當我們利用XMLHttpRequest對象從本地服務器獲取數據時是可以的,但是它不允許跨服務器發送請求。既然XMLHttpRequest因為考慮到安全性不允許發送請求到外部服務器,只好尋找其它的辦法。(見2.2。

說到AJAX首先要思考的兩個問題,第一個是AJAX以何種格式來交換數據?第二個是跨域的需求如何解決?這兩個問題目前都有不同的解決方案,比如數據可以用自定義字符串或者用XML來描述,跨域可以通過服務器端代理來解決。但到目前為止最被推崇或者說首選的方案還是用JSON來傳數據,靠JSONP來跨域。

JSON(JavaScript Object Notation)和JSONP(JSON with Padding)雖然只有一個字母的差別,但其實他們根本不是一回事兒:JSON是一種數據交換格式,而JSONP是一種依靠開發人員的聰明才智創造出的一種非官方跨域數據交互協議。我們拿最近比較火的諜戰片來打個比方,JSON是地下黨們用來書寫和交換情報的“暗號”,而JSONP則是把用暗號書寫的情報傳遞給自己同志時使用的接頭方式。看到沒?一個是描述信息的格式,一個是信息傳遞雙方約定的方法。

2.1 JSON

JSON:javaScript對象表示法(JavaScript Object Notation)

JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript, it can be used in the language with no muss or fuss.

 JSON是一種輕量級的數據交換格式。(json.org

JSON是存儲和交換文本信息的語法,類似XML。它采用鍵值對的方式來組織,易於人們閱讀和編寫,同時也易於機器解析和生成。JSON是獨立於語言的,也就是說不管什么語言,都可以解析json,只需要按照json的規則來就行。

JSON語法規則:JSON能夠以非常簡單的方式來描述數據結構,XML能做的它都能做,因此在跨平台方面兩者完全不分伯仲。

(1)JSON只有兩種數據類型描述符,大括號{}和方括號[],其余英文冒號:是映射符,英文逗號,是分隔符,英文雙引號""是定義符。

(2)大括號{}用來描述一組“不同類型的無序鍵值對集合”(每個鍵值對可以理解為OOP的屬性描述),方括號[]用來描述一組“相同類型的有序數據集合”(可對應OOP的數組)。

(3)上述兩種集合中若有多個子項,則通過英文逗號,進行分隔。

(4)鍵值對以英文冒號:進行分隔,並且建議鍵名都加上英文雙引號"",以便於不同語言的解析。

(5)JSON內部常用數據類型有字符串、數字、布爾、日期、null 等,字符串必須用雙引號引起來,其余的都不用,日期類型比較特殊,建議如果客戶端沒有按日期排序功能需求的話,那么把日期時間直接作為字符串傳遞就好,可以省去很多麻煩。

json解析的方法有兩種:eval()和parse()方法。————建議盡量使用JSON.parse方法來解析json里的字符串。

JSON和XML比較:

(1)JSON的長度和XML格式比起來很短小;

(2)JSON讀寫的速度更快;

(3)JSON可以使用JavaScript內建的方法直接進行解析,轉換成JavaScipt對象,非常方便

JSON的優點:

(1)基於純文本,跨平台傳遞極其簡單;

(2)Javascript原生支持,后台語言幾乎全部支持;

(3)輕量級數據格式,占用字符數量極少,特別適合互聯網傳遞;

(4)可讀性強,容易編寫和解析;

JSON的缺點:

(1)JSON在服務端語言的支持不像XML那么廣泛,不過JSON.org上提供很多語言的庫。

(2)如果你使用eval()來解析的話,會容易出現安全問題。

盡管如此,JSON的優點還是很明顯的。他是Ajax數據交互的很理想的數據格式。

2.2 JSONP

JSONP:JSON with Padding(填充式 JSON 或參數式 JSON)JSONP是一個非官方的協議,它允許在服務器端集成Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)

2.2.1 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數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。

一句話總結:由於同源策略的限制,XmlHttpRequest只允許請求當前源(域名、協議、端口)的資源,為了實現跨域請求,可以通過script標簽實現跨域請求,然后在服務端輸出JSON數據並執行回調函數,從而解決了跨域的數據請求。

以下兩篇文章以淺顯循序漸進的方式對jsonp的概念進行了闡釋,對理解其來龍去脈很有幫助:

說說JSON和JSONP,也許你會豁然開朗

jsonp其實很簡單【ajax跨域請求】

 2.2.2 JSONP的實現

JSONP借助了script標簽節點跨域訪問/獲取的特性。JSONP實現跨域請求的原理簡單的說,就是動態創建<script>標簽,然后利用<script>的src 不受同源策略約束來跨域獲取數據。

JSONP由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數。回調函數的名字一般是在請求中指定的。而數據就是傳入回調函數中的 JSON 數據。

a域名去聲明一個方法,b域名去調用這個方法,通過script標簽可以向不同域名提交http請求。

一、原生js實現jsonp

 1.最簡單的一種,客戶端(a域名)的html文件

<!doctype html>
<html lang="en">
<head></head>
<body>
    <script type="text/javascript">
        function jsonpCallback(result) {   
            for(var i in result) {  
                console.log(i+":"+result[i]);//循環輸出result的元素  
            }  
        }  
    </script>
    <script type="text/javascript" src="http://180.167.10.100/update/index.php?callback=jsonpCallback"></script>  
    <!-- 傳遞固定參數的方式 -->
    <!-- <script type="text/javascript" src="http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660&callback=jsonpCallback"></script> -->  
</body>
</html>

或者

<!doctype html>
<html lang="en">
<head></head>
<body>
    <script type="text/javascript">
        function jsonpCallback(result) {  
            //alert(result);  
            for(var i in result) {  
                console.log(i+":"+result[i]);//循環輸出result的元素  
            }  
        }  
        var JSONP=document.createElement("script");  
        JSONP.type="text/javascript";  
        JSONP.src="http://180.167.10.100/update/index.php?callback=jsonpCallback";  
        document.getElementsByTagName("head")[0].appendChild(JSONP); 
        /*傳遞參數的方式*/
        /*JSONP.src="http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660&callback=jsonpCallback";*/  
    </script>
</body>
</html>    

服務端(b域名)的php代碼:

<?php    
    //服務端返回JSON數據  
    //$arr=array('a'=>1,'b'=>2,'c'=>3); 
    $arr->IsLatestVersion = False;
    $arr->version = ‘3.3.2.764’;
    $arr->url = "http://172.30.28.18/update/releases/3.2.1.601/Windows/cdos-browser2_3.2.1.601.exe"; 
    $result=json_encode($arr);  
    //動態執行回調函數  
    $callback=$_GET['callback'];  
    echo $callback."($result)";  
?>

 2.在實際的項目中,我們的jsonp一般不會直接在html文件中實現,因為向服務器獲取數據的時機需要根據我們項目的實際需求來決定,所以一般需要動態的在我們所開發的模塊或某個js文件中創建script標簽以及傳遞相關參數(此參數也可能是項目過程中動態生成的),這時我們可以在客戶端(a域名)的js文件中如下實現:

// 得到查詢結果后的回調函數
function jsonpCallback(result) {  
    for(var i in result) {  
        console.log(i+":"+result[i]);//循環輸出result的元素  
    }  
}
function getVersionInfo(){
    var os = "Windows";
    var version = "3.2.3.660";
    var head = document.getElementsByTagName('head')[0];         
    var script = document.createElement('script');//創建script標簽,設置其屬性         
    var url = "http://180.167.10.100/update/index.php?os="+os+"&version="+version+"&callback=jsonpCallback";
    script.type = "text/javascript";                            
    script.setAttribute('src', url);//script.src= url;//提供jsonp服務的url地址
    head.appendChild(script);// 把script標簽加入head,此時調用開始
}
getVersionInfo();    //具體根據實際情況在合適位置調用即可

但是此時很可能出現報錯“jsonpCallback未定義”,原因其實很簡單,作為jsonp的回調函數,jsonpCallback必須是全局函數,而一般由於項目的模塊化和封裝我們的函數都是局部函數,此時我們必須將其全局化,可以將此回調函數jsonpCallback單拿出來放在html文件中,或者將其通過如下方法綁定到window對象上實現全局化:

(function(){        
    // 得到查詢結果后的回調函數
    window['jsonpCallback'] = function(data){
        for(var i in result) {  
            console.log(i+":"+result[i]);//循環輸出result的元素  
        }
    };
    function getVersionInfo(){
        var os = "Windows";
        var version = "3.2.3.660";
        var head = document.getElementsByTagName('head')[0];         
        var script = document.createElement('script');//創建script標簽,設置其屬性         
        var url = "http://180.167.10.100/update/index.php?os="+os+"&version="+version+"&callback=jsonpCallback";
        script.type = "text/javascript";                            
        script.setAttribute('src', url);//script.src= url;//提供jsonp服務的url地址
        head.appendChild(script);// 把script標簽加入head,此時調用開始
    }
    getVersionInfo();//具體根據實際情況在合適位置調用即可
})();

這樣jsonp的原理就很清楚了,首先在客戶端注冊一個callback(名字任意),然后動態創建script標簽通過src引入服務器端的php文件(類似引入js文件的方式),同時將客戶端注冊的callback的名字傳給服務器,php文件載入成功后,服務器先生成我們需要的 json 數據,然后將其作為參數傳入我們在url參數中指定的函數,所以jsonp是需要服務器端的頁面進行相應的配合的。

二、jQuery實現jsonp

jQuery本身就支持JSONP,jQuery封裝的$.ajax中有一個dataType屬性,將該屬性設置成dataType:"jsonp",就能指定按照jsonp方式訪問遠程服務從而實現JSONP跨域了。

注意:雖然jQuery將JSONP封裝在$.ajax中,但是其本質與$.ajax不一樣。如果設為dataType: 'jsonp',這個$.ajax方法就和ajax XmlHttpRequest沒什么關系了,取而代之的則是JSONP協議。

注:jsonp的方式只是針對get請求方式,不支持post請求。這也是Jsonp方式的局限性

實現原理與用原生js是一樣的,只不過我們不需要手動的插入script標簽以及定義回調函數,jquery在處理jsonp類型的ajax時(雖然jquery也把jsonp歸入了ajax,但其實不是一回事兒),會自動幫我們生成回調函數並把數據取出來供success屬性方法來調用。

而如果我們想指定自己的回調函數,或者說服務上規定了固定回調函數名怎么辦呢?只要如下所示添加一個jsonpCallback的選項,就可以指定我們自己的回調方法名myCallback,遠程服務接受callback參數的值就不再是自動生成的回調名,而是myCallback

jsonp及jsonpCallback的解釋:

更多參數請參考:jQuery.ajax()

客戶端(a域名)的js代碼實現如下:

$(document).ready(function(){
    $.ajax({
        url: "http://180.167.10.100/update/index.php",
        /*url: "http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660",*//*也可以直接在url里面傳遞數據*/
        type: "GET",
        dataType: "jsonp",
jsonp: "callback",//用於指示后台(php)獲取數據
jsonpCallback: "myCallback",//用於添加自己的回調函數,無此項則回調函數默認為success
async:
false, data: { 'version': "3.2.3.660", 'os': "Windows", }, timeout:3000, success: function(result) { console.log(result); for(var i in result) { console.log(i+":"+result[i]);//循環輸出result的元素 } }, error: function(xhr) { console.log(xhr); }, }); });

服務器端(b域名)的php代碼實現如下:

<?php
header('Content-Type: text/html; charset=utf-8');
/*獲取客戶端的數據*/
$callback=$_GET['callback'];/*注意跟客戶端指定的jsonp的值一致*/
$os = $_GET['os'];
$version = $_GET['version'];

/*對數據的操作--省略*/
/*對數據的操作--省略*/

/*將數據返回給客戶端*/
$response->IsLatestVersion = False;
$response->version = '3.3.2.764';
$response->url = 'http://180.167.10.100/update/releases/3.3.2.764/Windows/cdos-browser2_update_3.3.2.764.exe'; 
$responseJSON = json_encode($response);
echo $callback."($responseJSON)";
?>

三、$.getJSON方法實現jsonp

$.getJSON()是專門為ajax獲取json數據而設置的,並且支持"跨域"調用,其語法的格式為: getJSON(url,[data],[callback])     

實現原理與用原生js是一樣的,同樣我們不需要手動的插入script標簽以及定義回調函數,jquery會自動生成一個全局函數來替換callback=?中的問號,之后獲取到數據后又會自動銷毀,實際上就是起一個臨時代理函數的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就調用普通的ajax方法;跨域的話,則會以異步加載js文件的形式來調用jsonp的回調函數。

客戶端代碼實現如下:

//JQuery JSONP Support  
var url = "http://www.mydomain.com/api/suggest.php?symbol=IBM&callback=?";  
$.getJSON(url, function(result){  
    for(var i in result) {  
    console.log(i+":"+result[i]);//循環輸出result的元素  
    } 
});

2.2.3 ajax和jsonp的區別

1、ajax和jsonp這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個url,然后把服務器返回的數據進行處理,因此jquery和ext等框架都把jsonp作為ajax的一種形式進行了封裝;

2、但ajax和jsonp其實本質上是不同的東西。ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標簽來調用服務器提供的js腳本。

3、所以說,其實ajax與jsonp的區別不在於是否跨域,ajax通過服務端代理一樣可以實現跨域,jsonp本身也不排斥同域的數據的獲取。

4、還有就是,jsonp是一種方式或者說非強制性協議,如同ajax一樣,它也不一定非要用json格式來傳遞數據,如果你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。

總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變這一點!

2.2.4 $.get、$.post、$getJSON、$ajax的用法跟區別

首先,$.ajax()是jquery中通用的一個ajax封裝,$.get()和$.post()其實都是$.ajax()的一種,在$.ajax()中有一個type屬性,專門用來指定是get請求還是post請求的。

$.getJSON()是專門為ajax從服務器獲取json數據而設置的,並且支持"跨域"調用。jQuery的getJSON()函數,只是設置了JSON參數的ajax()函數的一個簡化版本。這個函數也是可以跨域使用的,相比get()、post()有一定優勢。JSON是一種理想的數據傳輸格式,它能夠很好的融合與JavaScript或其他宿主語言,並且可以被JS直接使用。使用JSON相比傳統的通過 GET、POST直接發送“裸體”數據,在結構上更為合理,也更為安全。
注意:$.getJSON()數據最終還是通過get方式發送數據出去的,這就決定了,發送的data數據量不能太多,否則造成url太長接收失敗(getJSON方式是不可能有post方式遞交的)。     

具體區別如下:

1.$.ajax()      
$.ajax()是jquery中通用的一個ajax封裝,其語法的格式為:      
$.ajax(options)      
其中options是一個object類型,它指明了本次ajax調用的具體參數,這里我把最常用的幾個參數附上     

$.ajax({      
    url: 'submit.aspx',      
    datatype: "json",      
    type: 'post',      
    success: function (e) {      
        //成功后回調      
        alert("回調函數成功了");      
     },      
    error: function(e){      
        //失敗后回調      
        alert("服務器請求失敗");      
    },      
    beforeSend: function(){      
        //發送請求前調用,可以放一些"正在加載"之類額話      
        alert("正在加載");           
    }
})

2.$.get      
    $.get()方法使用GET方式來進行異步請求,它的語法結構為:      
    $.get( url [, data] [, callback] )      
    解釋一下這個函數的各個參數:      
    url:string類型,ajax請求的地址。      
    data:可選參數,object類型,發送至服務器的key/value數據會作為QueryString附加到請求URL中。      
    callback:可選參數,function類型,當ajax返回成功時自動調用該函數。

$.get("submit.php",{id:'123',name:'小王',},function(data,state){              
    //這里顯示從服務器返回的數據            
    alert(data);          
    //這里顯示返回的狀態                
    if(state == 'ok'){      
        alert("返回數據成功");      
    }else{      
        alert("返回數據失敗");      
    }      
});

3.$.post()      
    $.post()方法使用POST方式來進行異步請求,它的語法結構為:      
    $.post(url,[data],[callback],[type])      
    這個方法和$.get()用法差不多,唯獨多了一個type參數,那么這里就只介紹type參數吧,其他的參考上面$.get()的。      
    type:type為請求的數據類型,可以是html,xml,json等類型,如果我們設置這個參數為:json,那么返回的格式則是json格式的,如果沒有設置,就和$.get()返回的格式一樣,都是字符串的。

$.post("submit.php",{id:'123',name:'小明',},function(data,state){              
    //這里顯示從服務器返回的數據             
    alert(data);        
    //這里顯示返回的狀態            
    if(state == 'ok'){      
        alert("返回數據成功");      
    }else{      
        alert("返回數據失敗");      
    },
"json");

4.$.getJSON()      
    $.getJSON()是專門為ajax獲取json數據而設置的,並且支持"跨域"調用,其語法的格式為:      
    getJSON(url,[data],[callback])      
    url:string類型, 發送請求地址      
    data :可選參數, 待發送 Key/value 參數 ,同get,post類型的data      
    callback :可選參數,載入成功時回調函數,同get,post類型的callback

2.2.4 JSONP的優缺點

JSONP使用起來方便,是目前比較流行的跨域方式,它的優缺點如下:

JSONP的優點

(1)它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制,JSONP可以跨越同源策略;
(2)它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持
(3)在請求完畢后可以通過調用callback的方式回傳結果。將回調方法的權限給了調用方。這個就相當於將controller層和view層終於分開了。我提供的jsonp服務只提供純服務的數據,至於提供服務以 后的頁面渲染和后續view操作都由調用者來自己定義就好了。如果有兩個頁面需要渲染同一份數據,你們只需要有不同的渲染邏輯就可以了,邏輯都可以使用同 一個jsonp服務。

JSONP的缺點:適用范圍太小(只能GET), 有安全風險(返回的代碼會直接執行)
(1)它只支持GET請求而不支持POST等其它類型的HTTP請求
(2)它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。
(3)jsonp在調用失敗的時候不會返回各種HTTP狀態碼,要確定 JSONP 請求是否失敗並不容易。雖然 HTML5 給<script>元素新增了一個 onerror事件處理程序,但目前還沒有得到任何瀏覽器支持。為此,開發人員不得不使用計時器檢測指定時間內是否接收到了響應。
(4)存在安全風險。因為JSONP 是從其他域(服務端)中加載代碼並直接執行,如果其他域不安全,很可能會在響應中夾帶一些惡意代碼,於是所有調用這個 jsonp的網站都會存在漏洞,無法把危險控制在一個域名下,而此時除了完全放棄 JSONP 調用之外,沒有辦法追究。因此在使用不是你自己運維的 Web 服務時,一定得保證它安全可靠。

 3、解決跨域問題-CORS/XHR2

3.1 概念

為了改善網絡應用程序,開發人員要求瀏覽器供應商允許跨域請求。

CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-Origin Resource Sharing),它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

跨域請求主要用於:

  • 調用XMLHttpRequest或fetchAPI通過跨站點方式訪問資源
  • 網絡字體,例如Bootstrap(通過CSS使用@font-face 跨域調用字體)
  • 通過canvas標簽,繪制圖表和視頻。

HTML5提供的XMLHttpRequest Level2已經實現了跨域訪問以及其他的一些新功能,CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。

整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。因此,實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。

3.2 實現

CORS背后的基本思想是使用自定義的HTTP頭部允許瀏覽器和服務器相互了解對方,從而決定請求或響應成功與否.

Access-Control-Allow-Origin:指定授權訪問的域
Access-Control-Allow-Methods:授權請求的方法(GET, POST, PUT, DELETE,OPTIONS等)

如在服務器端添加如下代碼即可:
header('Access-Control-Allow-Origin:*');//設置可以訪問的域,星號代表所有域都可以訪問
header('Access-Control-Allow-Methods:POST,GET');

配合XHR2的IE10以下跨域:
在代碼中js加上一句jQuery.support.cors =true;或者$.support.cors =true; 
然后:設置IE瀏覽器->Internet選項->安全->自定義級別->其他選項下面的->通過源數據:選擇“啟用”或者“提示”

詳細可參考:

跨域資源共享 CORS 詳解

利用CORS實現跨域請求

CORS——跨域請求那些事兒

CORS 跨域 實現思路及相關解決方案

3.3 CORS 對比 JSONP

都能解決 Ajax直接請求普通文件存在跨域無權限訪問的問題

(1)JSONP只能實現GET請求,而CORS支持所有類型的HTTP請求

(2)使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理

(3)JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS!

4、解決跨域問題-代理

通過在同域名的web服務器端創建一個代理,用本地服務器的后台調用其他域的后台服務,然后把相應結果返回給前端,這樣前端調用同域名服務器的服務和其他域的服務一樣了。(由后台實現,了解

5、其他跨域方法

參考:

JavaScript跨域總結與解決辦法

js中幾種實用的跨域方法原理詳解

 


免責聲明!

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



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