通過XHR實現Ajax通信的一個主要限制,來源于跨域安全策略。解決跨域總結了幾下幾種方式:
一、跨源資源共享(CORS)
在發送請求時,請求頁面的源信息(協議、域名和端口)為:http://www.baidu.com
如果服務器認為這個請求可以接受,就在后臺代碼中設置Access-Control-Allow-Origin的值為 http://www.baidu.com(如果服務器認為任何請求都可以接受就設置為“*”)
二、JSONP
JSONP跨域的原理:
通過動態創建一個script標簽,script標簽不受同源策略(CORS)限制,調用服務器端js腳本,服務器會返回一個用傳過去的回調函數名包裹著服務器返回的json數據的函數執行,來執行相應的函數。
用原生js模擬jquery封裝的jsonp:
var $ = {}; $.ajax = function(param){ var url = param.url; var success = param.success; var callback = param.jsonpCallback; //callback是在全局作用域執行的 window[callback] = function(data) { success(data); } var script = document.createElement("script"); script.src = url + "&callback=" + callback; document.getElementsByTagName("head")[0].appendChild(script); }
注: JSONP僅適用于HTTP的GET請求
JSONP有兩點不足:
1、JSONP是從其他域中加載代碼執行。如果其他域不安全,很可能會在響應中夾帶一些惡意代碼,而此時除了完全放棄JOSNP調用之外,沒有辦法追究
2、要確定JSONP請求是否失敗應不容易
三、圖像Ping(使用img標簽)
一個網頁可以從任何網頁中加載圖像,不用擔心跨域不跨域。
圖像Ping是與服務器進行簡單、單向的跨域通信的一種方式。請求的數據是通過查詢字符串形式發送的,而響應可以是任意內容,但通常是像素圖或204響應。通過圖像Ping,瀏覽器得不到任何具體的數據,但通過監聽load和error事件,能知道響應什么時候接收到。
來看下面的例子:
var img = new Image(); img.onload = img.onerror = function() { alert("Done!"); }; img.src = "http://www.example.com/test?name=Nicholas";
這里創建了一個Image的實例,然后將onload和onerror事件處理程序指定為同一個函數。這樣無論是什么響應,只要請求完成,就能得到通知。
注:圖像Ping最常用于跟蹤用戶點擊頁面或動態廣告曝光次數。
圖像Ping有兩個主要的缺點:
1、只能發送GET請求
2、無法訪問服務器的響應文本
四、window.postMessage和iframe
window.postMessage的功能是允許程序員跨域在兩個窗口或者frames間發送數據信息。基本上,它就像是跨域的Ajax,但不是瀏覽器跟服務器之間交互,而是在兩個客戶端之間通信。除了IE6、IE7之外的所有瀏覽器都支持這個功能。
比較重要的事件:
source - 消息源,消息的發送窗口或者iframe
origin - 消息源的URI(可能包含協議、域名和端口),用來驗證數據源
data - 發送方發送給接收方的數據
注:盡管Internet Explorer8和Internet Explorer9都實現了postMessage API,但它們也有一些局限性。最明顯的一點就是它們只允許向iframe元素發送消息。如果嘗試向特定的窗口或選項卡發送消息,都會提示“No such interface supported”。
示例:
頁面A.html在webstorm里,domain="http://localhost:9090"
頁面B.html在eclipse里,domain = "http://localhost:8080"
示例一:
A頁面里的iframe嵌套B頁面,A頁面給B頁面(外面給里面)發送內容,B頁面給A頁面返回內容,代碼如下:
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>A給B發送</title>
</head>
<body>
A-receive:<span id="receive"></span>
<button onclick="send()">點擊</button>
<iframe id="myIframe" src="http://localhost:8080/col/baidu-demo/html/receive1.html" frameborder="0"></iframe>
<script>
`
var domain = "http://localhost:8080";
var message = 'hello,B';
var iframe = document.getElementById("myIframe").contentWindow;
function send() {
iframe.postMessage(message, domain);
}
window.addEventListener('message', function(event) {
if(event.origin != "http://localhost:8080") return;
console.log('received:' + event.data, event);
document.getElementById("receive").innerHTML = event.data;
},false);
</script> </body> B.html代碼如下: <head> <meta charset="UTF-8"> <title>A給B發送</title> </head> <body> <div>hello</div> B-receive:<span id=receive></span> <script>
var domain = "http://localhost:9090";
window.addEventListener('message', function(event) {
if(event.origin != "http://localhost:9090") return;
document.getElementById("receive").innerHTML = event.data;
event.source.postMessage("hello,A", domain);
},false);
`
</script>
</body>
示例二:
A頁面里的iframe嵌套B頁面,B頁面給A頁面(里面給外面)發送內容,代碼如下:
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>B給A發</title>
</head>
<body>
A-receive:<span id="receive"></span>
<iframe id="myIframe" src="http://localhost:8080/col/baidu-demo/html/receive.html" frameborder="0"></iframe>
<script>
window.addEventListener('message', function(event) { if(event.origin != "http://localhost:8080") return; console.log('received:' + event.data, event); document.getElementById("receive").innerHTML = event.data; },false);
</script>
</body>
B.html代碼如下:
<head>
<meta charset="UTF-8">
<title>給A發</title>
</head>
<body>
<div>hello</div>
<button onclick="send()">點擊</button>
<script>
var domain = "http://localhost:9090"; var message = 'hello,A'; console.log('blog.local:sending message:' + message); function send() { parent.postMessage(message, domain); }
</script>
</body>
示例三:
A頁面打開B頁面,A頁面給B頁面發送內容,B頁面給A頁面返回內容
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>A給B發</title>
</head>
<body>
A-receive:<span id="receive"></span>
<button onclick="send()">點擊</button>
<script>
`
var domain = "http://localhost:8080";
var message = 'hello,B';
var openPage = window.open("http://localhost:8080/col/baidu- demo/html/receive2.html");
function send() {
openPage.postMessage(message, domain);
};
window.addEventListener('message', function(event) {
if(event.origin != "http://localhost:8080") return;
document.getElementById("receive").innerHTML = event.data;
},false);
</script> </body> B.html代碼如下: <head> <meta charset="UTF-8"> <title>A給B發</title> </head> <body> <div>hello</div> B-receive:<span id=receive></span> <script>
var domain = "http://localhost:9090";
window.addEventListener('message', function(event) {
if(event.origin != "http://localhost:9090") return;
document.getElementById("receive").innerHTML = event.data;
window.opener.postMessage("hello,A",domain);
},false);
`
</script>
</body>
參考文獻:
http://www.webhek.com/window-postmessage-api/
《JavaScript高級程序設計》(第三版)