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