JavaScript跨域資源請求(CORS)解決方案


跨域:當協議、主域名、子域名、端口號中任意一個不相同時都不算同一個域,而在不同域之間請求數據即為跨域請求。解決方法有以下幾種(如有錯誤歡迎指出)以請求圖片url為例:

1.通過XMLHttpRequest對象實現(IE10以下不支持)

XMLHttpRequest2.0已經實現了對CORS的原生支持,只需要在訪問資源的時候使用絕對URL即可,需要在服務器端將頭信息“Access-Control-Origin"設為”*“或當前域名的地址:

html&javascript:

 1     <button id="btn">load</button>
 2     <div id="process"></div>
 3     <div id="img"></div>
 4 
 5     <script>
 6         var btn = document.getElementById('btn'),
 7             pro = document.getElementById('process'),
 8             pic = document.getElementById('img');
 9 
10         btn.onclick = function() {

12 var xhr = new XMLHttpRequest(); 17 xhr.onreadystatechange = function() { 18

19 if(xhr.readyState == 4 && xhr.status == 200){ 20
21 var img = document.createElement('img'); 22 img.src = JSON.parse(xhr.responseText).src; 23 pic.appendChild(img); 24 } 25 } 26 xhr.open('GET','http://127.0.0.1:8085/AJAX/ajax/server.php',true); 27 xhr.send(null); 28 } 29 </script>

這個例子是在本地測試的,html文件所在的位置是localhost:8085下,雖然127.0.0.1也是指的localhost,但他們也不是同一個域,可以利用這兩個地址來模擬跨域請求。

php:

<?php
header("Content-Type:text/plain");
header("Access-Control-Allow-Origin:http://localhost:8085");//設置頭部,不設置的話請求會被拒絕
echo '{"src":"http://www.pinkbluecp.cn/face_alignment/img/picture.jpg"}';
?>

post請求也差不多:

btn.onclick = function() {
            if(typeof XMLHttpRequest != 'undefined'){
                var xhr = new XMLHttpRequest();
            }else if(typeof ActiveXObject != 'undefined'){
                var xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }

            xhr.onreadystatechange = function() {
                console.log(1)
                if(xhr.readyState == 4 && xhr.status == 200){
                    console.log(2)
                    pic.innerHTML = xhr.responseText;
                }
            }
            xhr.open('POST','http://127.0.0.1:8085/AJAX/ajax/server.php',true);
            xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");    //需要設置下頭部
            var str = 'active=login';
            xhr.send(str);

php

<?php
header("Content-Type:text/plain");
header("Access-Control-Allow-Origin:http://localhost:8085");

if($_POST['active'] == 'login'){
    echo 'success';
}else {
    echo "error";
}
?>

發送數據需要設置一下頭部的Content-Type字段。

2.IE(8-10)中實現CORS:

IE8中引入了XDomainRequest對象,該對象和XHR相似,但它能實現安全可靠的跨域通信,不過在IE11中已經無法使用了。

 1 var btn = document.getElementById('btn'),
 2             pro = document.getElementById('process'),
 3             pic = document.getElementById('img');
 4 
 5         btn.onclick = function() {
 6             var xhr = new XDomainRequest();
 7             xhr.onload = function() {
 8                 var img = document.createElement('img');
 9                 console.log(xhr.responseText)
10                 // img.src = JSON.parse(xhr.responseText).src;
11                 img.src = xhr.responseText;
12                 console.log(img.src);
13                 pic.appendChild(img);
14             };
15             xhr.onerror = function() {  //XDR無法確定響應狀態,需要添加onerror來檢測錯誤
16                 alert("Error!");
17             }
18             xhr.open('GET','http://127.0.0.1:8085/AJAX/ajax/server.php'); //該對象的的請求都是異步,沒有第三個參數
19             xhr.send(null);
20         }        

XDR與XHR不同之處在於:

a. cookie不會隨請求發送,也不會隨響應返回

b. 只能設置請求頭中的Content-Type字段

c. 不能訪問響應頭部信息

d. 只支持GET和POST請求

兩者的方法也略有不同,XDR的open方法只有異步一種狀態,所以只要傳兩個參數method和url,在請求返回之后會觸發load事件,但無法獲取響應狀態碼,所以想判斷請求是否成功只能添加error事件。它也可以和XHR一樣添加超時。

關於XDR的post請求,雖然js高編上有介紹,但是在多次嘗試后發現已經行不通了,高編上說XDR有專門的屬性contentType來設置請求頭信息,但在瀏覽其中會報錯:

無法進行設置contentType的操做,后來查閱了一下這篇博客,貌似XDR已經無法在設置請求頭了,詳情可以去看原博。

3. 實現跨瀏覽器的CORS

js高編上有一段實現跨瀏覽器的CORS實現函數:

    <button id="btn">load</button>
    <div id="process"></div>
    <div id="img"></div>

    <script>
        var btn = document.getElementById('btn'),
            pic = document.getElementById('img');

        btn.onclick = function() {
            console.log(1);
            var xhr = createCORS('GET','http://127.0.0.1:8085/AJAX/ajax/server.php');
            if(xhr){
                console.log(2)
                xhr.onload = function(){
                    console.log(3)
                    var img = document.createElement('img');
                    img.src = xhr.responseText;
                    pic.appendChild(img);
                };
                xhr.onerror = function() {
                    alert("Error");
                };
                xhr.send(null);
            }
        }

        function createCORS(method,url){  //參考js高編
            console.log('fun')
            var xhr = new XMLHttpRequest();
            if('withCredentials' in xhr){    //檢測是否含有憑據屬性
                xhr.open(method,url,true);
            }else if(typeof XDomainRequest != 'undefined'){    //兼容ie
                xhr = new XDomainRequest();
                xhr.open(method,url);
            }else {
                xhr = null;
            }
            return xhr;
        }
    </script>

除了IE10-外所有瀏覽器都有withCredentials屬性,所以可以根據這個屬性來判斷是否支持XMLHttpRequest2.0.

4.圖片Ping

圖片Ping就是利用圖片的src可以使用跨域資源的特性來實現,但是只能實現簡單的單向請求,在img的src中傳入一個其他域的url並附帶一些參數。

5.JSONP

JSONP和圖片Ping很相似,也是利用script標簽中的鏈接不受同源限制可以向不同域傳遞信息,但它傳遞的是一個已經存在的回調函數,服務器接收到后,將響應數據傳入該回調函數並執行該函數從而達到獲取數據的目的。

先來看下<script>標簽中傳入src得到的是什么:

<script src="test.txt"></script>

在src中傳入一個文本文件

瀏覽器中報語法錯誤,hello world這個變量未定義,服務器響應的數據就時test.txt的內容,而script把這段純文本當作js代碼來解析,當在test.txt中將這段文字加上單引號后:

瀏覽器不報錯了,因為把它當作字符串來解析了。現在把鏈接換成一個php文件:

php

<?php
echo "Hello World!";
?>

結果一樣,瀏覽器同樣也會報錯,加上引號后同樣也不報錯,如果這時服務器端返回的是一個函數名,而該頁面正好又有一個同名函數會怎樣:

php:

<?php
echo "callback('hello world')";
?>

html

<body>
    <script>
        function callback(res){
            alert(res);
        }
    </script>
    <script src="jsonp.php"></script>
</body>

結果:

 

 callback函數被執行了,因為后台的響應數據被script標簽當作js代碼執行了,所以這就能理解jsonp的原理了,利用這個回調函數可以獲得后台傳來的數據:

html:

<body>
    <script>
        function jsonp(res){
            for(var i in res){
                console.log("key:"+i+";value:"+res[i]);
            }
        }
    </script>
    <script src="jsonp.php?callback=jsonp"></script>  //可動態創建
</body>

php:

<?php
$str = '{"name":"Lee","age":20}';
$callback = $_GET['callback'];
echo $callback."($str)";
?>

上面一個簡單的例子展示了jsonp如何用回調函數獲取服務器響應的json數據:

 

 JSONP的有點在於可以訪問到服務器的響應文本,不過缺點就是要從其他域加載代碼執行,必須要保證其他域的安全性,不然可能響應信息中會附帶惡意腳本,還有一點就是無法確定請求是否失敗,即使失敗也不會有提示。

6.iframe跨域

iframe跨域與jsonp相似,也利用了src不受同源限制的特性。當A域下的x.html頁面要訪問B域下的y.html中的信息可以通過A域下的z.html頁面作為代理來獲取信息:

x.html: 

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>無標題文檔</title>
 6 </head>
 7 
 8 <body>
 9 <iframe src="http://127.0.0.1:8085/AJAX/ajax/proxya.html" style="display: none"></iframe>
10 <p id="getText"></p>
11 <script>
12     function callback(text){
13         text = JSON.parse(decodeURI(text));
14         document.getElementById("getText").innerHTML= '姓名:' + text.name + '; 年齡:' + text.age;    
15     }
16 </script>
17 </body>
18 </html>

y.html:

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>無標題文檔</title>
 6 </head>
 7 
 8 <body>
 9 <iframe id="myfarme" src="###"></iframe>
10 <script>
11     window.onload = function(){
12         var text = '{"name":"cheng","age":22}';  //存儲一個json格式的數據
13         document.getElementById('myfarme').src="http://localhost:8085/AJAX/ajax/proxyb.html?content="+encodeURI(text); //將數據傳個代理頁面處理,此時src中的地址不受同源限制,與jsonp相似
14     }
15 </script>
16 </body>
17 </html>

z.html:

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>無標題文檔</title>
 6 <script>
 7 window.onload = function(){
 8     var text = window.location.href.split('=')[1];  //通過分解url獲取y.html傳來的參數
9 top.callback(text);  //調用x.html的callback函數 10 } 11 </script> 12 </head> 13 14 <body> 15 </body> 16 </html>

 


免責聲明!

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



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