詳解Javascript幾種跨域方式總結(jié)
在客戶端編程語言中如javascript,同源策略規(guī)定跨域之間的腳本是隔離的,一個域的腳本不能訪問和操作另外一個域的絕大部分屬性和方法。只有當兩個域具有相同的協(xié)議,相同的主機,相同的端口時,我們就認定他們是相同的域。可是在實際開發(fā)中我們經(jīng)常需要獲取其他域的資源,這個時候各種不同的跨域資源方式就各顯神通了,今天主要來總結(jié)一下工作中常用的幾種跨域方式,以備查詢。
1.window.name
window 對象的name屬性是一個很特別的屬性,當在 frame 中加載新頁面時,name 的屬性值依舊保持不變。那么我們可以在頁面 A中用iframe加載其他域的頁面B,而頁面B中用JavaScript把需要傳遞的數(shù)據(jù)賦值給window.name,iframe加載完成之后,此時 name 屬性值可被獲取到,以訪問 Web 服務(wù)發(fā)送的信息。但 name 屬性僅對相同域名的 frame 可訪問。這意味著為了訪問 name 屬性,當遠程 Web 服務(wù)頁面被加載后,必須導航 frame 回到原始域。即頁面A修改iframe的地址,將其變成同域的一個地址,然后就可以讀出window.name的值了。一旦 name 屬性獲得,銷毀 frame 。這個方式非常適合單向的數(shù)據(jù)請求,而且協(xié)議簡單、安全。
頁面B(www.jesse.com/data.html)代碼如下:
<script type="text/javascript"> window.name = 'I was there!'; // 這里是要傳輸?shù)臄?shù)據(jù),大小一般為2M,IE和firefox下可以大至32M左右 // 數(shù)據(jù)格式可以自定義,如json、字符串 </script>
頁面A(www.jack.com/index.html)代碼如下:
<script type="text/javascript">
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name; // 讀取數(shù)據(jù)
console.log(data); //彈出'I wasthere!'
(function(){
//獲取數(shù)據(jù)以后銷毀這個iframe。
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
})();
} else if (state === 0) {
state = 1;
// 設(shè)置的代理頁面使其回原始域
iframe.contentWindow.location = "http://www.jack.com/proxy.html";
}
};
iframe.src = 'http://www.jesse.com/data.html';
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
</script>
2.具備src的標簽
雖然瀏覽器默認禁止了跨域訪問,但并不禁止在頁面中用標簽的src屬性引用其他域的文件。根據(jù)這一點,可以方便地通過創(chuàng)建具有src屬性的節(jié)點方法來實現(xiàn)完全跨域的通信。使用這種原理的跨域方式有以下幾種:
動態(tài)創(chuàng)建script
例如我要從域A的頁面pageA加載域B的數(shù)據(jù),那么在域B的頁面pageB中我以JavaScript的形式聲明pageA需要的數(shù)據(jù),然后在 pageA中用script標簽把pageB加載進來,那么pageB中的腳本就會得以執(zhí)行。
pageA(www.jack.com/index.html)代碼如下:
function getData(data){
//這里是對獲取的數(shù)據(jù)的相關(guān)操作
console.log(data);
//數(shù)據(jù)獲取到后移除創(chuàng)建的script標簽
document.body.removeChild(originData);
}
var originData = document.createElement('script');
originData.src = 'http://www.jesse.com/data.js';
originData.setAttribute("type", "text/javascript");
document.body.appendChild(originData);
pageB(www.jesse.com/data.js)代碼如下:
getData('這里是遠程跨域獲取的數(shù)據(jù)');//數(shù)據(jù)格式可以自定義,如json、字符串
jsonp
在用$.ajax()獲取遠程數(shù)據(jù)時,如果是跨域資源則可以使用jsonp方法,以前一直以為jsonp是ajax的一種,后來才明白他們根本就不是一回事。ajax是以xhr方式請求數(shù)據(jù)的,而jsonp是以script方式請求數(shù)據(jù)的,這個就是和上面的動態(tài)創(chuàng)建script方式一樣。
pageA(www.jack.com/index.html)代碼如下:
$.ajax({
//JSONP不支持POST方式
type:"GET",
url:"http://www.jesse.com/data.php",
dataType:"jsonp",
//自定義的jsonp回調(diào)函數(shù)名稱,默認為jQuery自動生成的隨機函數(shù)名,也可以寫"?",jQuery會自動為你處理數(shù)據(jù)
jsonpCallback:"getData",
success: function(data){
console.log(data);
},
error: function(){
console.log('fail');
}
})
pageB(www.jesse.com/data.js)代碼如下:
<?php
$callback = $_GET['callback'];//得到回調(diào)函數(shù)名,這里是getData
$data = array('a','b','c');//要返回的數(shù)據(jù)
echo $callback.'('.json_encode($data).')';//輸出
?>
3.document.domain
對于主域相同而子域不同的例子,可以通過設(shè)置document.domain的辦法來解決。 具體的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html兩個文件中分別加上 document.domain = "a.com";然后通過a.html文件中創(chuàng)建一個iframe,去控制iframe的contentDocument,這樣兩個js文件之間就可以 “交互”了。當然這種辦法只能解決主域相同而二級域名不同的情況
www.a.com上的a.html
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在這里操縱b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
script.a.com上的b.html
document.domain = 'a.com';
4.跨域資源共享(CORS)
原理:跨源資源共享(CORS)定義一種跨域訪問的機制,可以讓AJAX實現(xiàn)跨域訪問。CORS允許一個域上的網(wǎng)絡(luò)應(yīng)用向另一個域提交跨域AJAX請求。實現(xiàn)此功能非常簡單,只需由服務(wù)器發(fā)送一個響應(yīng)標頭即可。它是通過客戶端+服務(wù)端協(xié)作聲明的方式來確保請求安全的。服務(wù)端會在HTTP請求頭中增加一系列HTTP請求參數(shù)(例如Access-Control-Allow-Origin等),來限制哪些域的請求和哪些請求類型可以接受,而客戶端在發(fā)起請求時必須聲明自己的源(Orgin),否則服務(wù)器將不予處理,如果客戶端不作聲明,請求甚至會被瀏覽器直接攔截都到不了服務(wù)端。服務(wù)端收到HTTP請求后會進行域的比較,只有同域的請求才會處理。
pageA(www.jack.com/index.html)代碼如下:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText);
}
};
xhr.open("get","http://www.jesse.com/data.php");
xhr.send(null);
pageB(www.jesse.com/data.php)代碼如下:
<?php
header("Access-Control-Allow-Origin: http://www.jack.com");//與簡單的請求相同
header("Access-Control-Allow-Methods: GET, POST");//允許請求的方法
header("Access-Control-Max-Age: 3628800"); //將這個請求緩存多長時間
$data = array('a','b','c');//要返回的數(shù)據(jù)
echo json_encode($data);//輸出
?>
5.window.postMesage 不常用
window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可以使用它來向其它的window對象發(fā)送消息,無論這個window對象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經(jīng)支持window.postMessage方法。
pageA(www.jack.com/index.html)代碼如下:
<iframe id="proxy" src="http://www.jesse.com/index.html" onload="postMsg()" style="display: none"></iframe>
<script type="text/javascript">
var obj = {
msg: 'hello world'
}
function postMsg() {
var iframe = document.getElementById('proxy');
var win = iframe.contentWindow;
win.postMessage(obj, 'http://www.jesse.com');
}
</script>
pageB(www.jesse.com/data.php)代碼如下:
<script type="text/javascript">
window.onmessage = function(e) {
console.log(e.data.msg + " from " + e.origin);
}
</script>
6. location.hash 不常用
pageA(www.jack.com/index.html)代碼如下:
function startRequest() {
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://www.jesse.com/b.html#sayHi'; //傳遞的location.hash
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
if (console.log) {
console.log('Now the data is ' + data);
}
} catch (e) {};
}
setInterval(checkHash, 5000);
window.onload = startRequest;
pageA(www.jack.com/proxy.html)代碼如下:
parent.parent.location.hash = self.location.hash.substring(1);
pageB(www.jesse.com/b.html)代碼如下:
function checkHash() {
var data = '';
//模擬一個簡單的參數(shù)處理操作
switch (location.hash) {
case '#sayHello':
data = 'HelloWorld';
break;
case '#sayHi':
data = 'HiWorld';
break;
default:
break;
}
data && callBack('#' + data);
}
function callBack(hash) {
// ie、chrome的安全機制無法修改parent.location.hash,所以要利用一個中間的www.a.com域下的代理iframe
var proxy = document.createElement('iframe');
proxy.style.display = 'none';
proxy.src = 'http://www.jack/c.html' + hash; // 注意該文件在"www.jack.com"域下
document.body.appendChild(proxy);
}
window.onload = checkHash;
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
filters.revealTrans.Transition使用方法小結(jié)
有看到幻燈片調(diào)用會用到divid.filters.revealTrans.Transition=Math.floor(Math.random()*23)和divid.filters.revealTrans.apply(),那么這兩個都是什么意思呢?2010-08-08
JavaScript簡單實現(xiàn)關(guān)鍵字文本搜索高亮顯示功能示例
這篇文章主要介紹了JavaScript簡單實現(xiàn)關(guān)鍵字文本搜索高亮顯示功能,涉及javascript基于事件響應(yīng)的頁面元素遍歷、分割、替換等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
解決前端接收 type:"application/octet-stream" 格
前端接收 type: “application/octet-stream“ 格式的數(shù)據(jù)并下載,還有后端既返回octet-stream還返回JSON數(shù)據(jù)時的處理方法,今天給大家分享前端接收 type:"application/octet-stream" 格式的數(shù)據(jù)并下載(解決后端返回不唯一問題)的解決方案,感興趣的朋友一起看看吧2023-12-12
原生js實現(xiàn)可兼容PC和移動端的拖動滑塊功能詳解【測試可用】
這篇文章主要介紹了原生js實現(xiàn)可兼容PC和移動端的拖動滑塊功能,結(jié)合實例形式詳細分析了javascript事件響應(yīng)及頁面元素屬性動態(tài)操作實現(xiàn)滑塊拖動功能的相關(guān)操作技巧,需要的朋友可以參考下2019-08-08
ES6標準 Arrow Function(箭頭函數(shù)=>)
ES6標準新增了一種新的函數(shù):Arrow Function(箭頭函數(shù)),為什么叫Arrow Function?因為它的定義用的就是一個箭頭2020-05-05

