通過XHR實現(xiàn)Ajax通信的一個主要限制萎羔,來源于跨域安全策略滔吠。解決跨域總結(jié)了幾下幾種方式:
一、跨源資源共享(CORS)
在發(fā)送請求時焕议,請求頁面的源信息(協(xié)議宝磨、域名和端口)為:http://www.baidu.com
如果服務(wù)器認為這個請求可以接受,就在后臺代碼中設(shè)置Access-Control-Allow-Origin的值為 http://www.baidu.com(如果服務(wù)器認為任何請求都可以接受就設(shè)置為“*”)
二盅安、JSONP
JSONP跨域的原理:
通過動態(tài)創(chuàng)建一個script標簽唤锉,script標簽不受同源策略(CORS)限制,調(diào)用服務(wù)器端js腳本别瞭,服務(wù)器會返回一個用傳過去的回調(diào)函數(shù)名包裹著服務(wù)器返回的json數(shù)據(jù)的函數(shù)執(zhí)行窿祥,來執(zhí)行相應(yīng)的函數(shù)。
用原生js模擬jquery封裝的jsonp:
var $ = {}; $.ajax = function(param){ var url = param.url; var success = param.success; var callback = param.jsonpCallback; //callback是在全局作用域執(zhí)行的 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是從其他域中加載代碼執(zhí)行晒衩。如果其他域不安全嗤瞎,很可能會在響應(yīng)中夾帶一些惡意代碼,而此時除了完全放棄JOSNP調(diào)用之外浸遗,沒有辦法追究
2猫胁、要確定JSONP請求是否失敗應(yīng)不容易
三、圖像Ping(使用img標簽)
一個網(wǎng)頁可以從任何網(wǎng)頁中加載圖像跛锌,不用擔(dān)心跨域不跨域弃秆。
圖像Ping是與服務(wù)器進行簡單、單向的跨域通信的一種方式髓帽。請求的數(shù)據(jù)是通過查詢字符串形式發(fā)送的菠赚,而響應(yīng)可以是任意內(nèi)容,但通常是像素圖或204響應(yīng)郑藏。通過圖像Ping,瀏覽器得不到任何具體的數(shù)據(jù)衡查,但通過監(jiān)聽load和error事件,能知道響應(yīng)什么時候接收到必盖。
來看下面的例子:
var img = new Image(); img.onload = img.onerror = function() { alert("Done!"); }拌牲; img.src = "http://www.example.com/test?name=Nicholas";
這里創(chuàng)建了一個Image的實例,然后將onload和onerror事件處理程序指定為同一個函數(shù)歌粥。這樣無論是什么響應(yīng)塌忽,只要請求完成,就能得到通知失驶。
注:圖像Ping最常用于跟蹤用戶點擊頁面或動態(tài)廣告曝光次數(shù)土居。
圖像Ping有兩個主要的缺點:
1、只能發(fā)送GET請求
2嬉探、無法訪問服務(wù)器的響應(yīng)文本
四擦耀、window.postMessage和iframe
window.postMessage的功能是允許程序員跨域在兩個窗口或者frames間發(fā)送數(shù)據(jù)信息∩蹋基本上眷蜓,它就像是跨域的Ajax,但不是瀏覽器跟服務(wù)器之間交互胎围,而是在兩個客戶端之間通信账磺。除了IE6、IE7之外的所有瀏覽器都支持這個功能痊远。
比較重要的事件:
source - 消息源垮抗,消息的發(fā)送窗口或者iframe
origin - 消息源的URI(可能包含協(xié)議、域名和端口)碧聪,用來驗證數(shù)據(jù)源
data - 發(fā)送方發(fā)送給接收方的數(shù)據(jù)
注:盡管Internet Explorer8和Internet Explorer9都實現(xiàn)了postMessage API冒版,但它們也有一些局限性。最明顯的一點就是它們只允許向iframe元素發(fā)送消息逞姿。如果嘗試向特定的窗口或選項卡發(fā)送消息辞嗡,都會提示“No such interface supported”捆等。
示例:
頁面A.html在webstorm里,domain="http://localhost:9090"
頁面B.html在eclipse里续室,domain = "http://localhost:8080"
示例一:
A頁面里的iframe嵌套B頁面栋烤,A頁面給B頁面(外面給里面)發(fā)送內(nèi)容,B頁面給A頁面返回內(nèi)容挺狰,代碼如下:
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>A給B發(fā)送</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發(fā)送</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頁面(里面給外面)發(fā)送內(nèi)容,代碼如下:
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>B給A發(fā)</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發(fā)</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頁面發(fā)送內(nèi)容薯定,B頁面給A頁面返回內(nèi)容
A.html代碼如下:
<head lang="en">
<meta charset="UTF-8">
<title>A給B發(fā)</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發(fā)</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高級程序設(shè)計》(第三版)