1、神馬是跨域(Cross Domain)
說白點就是post、get的url不是你當前的網站,域名不同。例如在aaa.com/a.html裡面,表單的提交action是bbb.com/b.html。
不僅如此,www.aaa.com和aaa.com之間也屬於跨域,因為www.aaa.com是二級域名,aaa.com是根域名。
JavaScript出於安全方面的考慮,是不允許跨域調用其他頁面的對象的(同源策略 Same-Origin Policy)。
關於JavaScript能否跨域通信的詳細說明,見下表:
http://www.a.com/a.js訪問以下URL的結果
URL | 說明 | 是否允許通信 |
---|---|---|
http://www.a.com/b.js | 同一域名下 | 允許 |
http://www.a.com/script/b.js | 同一域名下不同文件夾 | 允許 |
http://www.a.com:8000/b.js | 同一域名,不同端口 | 不允許 |
https://www.a.com/b.js | 同一域名,不同協議 | 不允許 |
http://70.32.92.74/b.js | 域名和域名對應ip | 不允許 |
http://script.a.com/b.js | 主域相同,子域不同 | 不允許 |
http://a.com/b.js | 同一域名,不同二級域名(同上) | 不允許 |
http://www.b.com/b.js | 不同域名 | 不允許 |
2、為嘛要跨域
跨域這東西其實很常見,例如我們可以把網站的一些腳本、圖片或其他資源放到另外一個站點。例如我們可以使用Google提供的jQuery,加載時間少瞭,而且減少瞭服務器的流量,如下
<script type="text/java script"src="https://aja x.googleapis.com/aj ax/libs/jquery/1.4.2/jquery.min.js"></script>
有時候不僅僅是一些腳本、圖片這樣的資源,我們也會希望從另外的站點調用一些數據(有時候是不得不這樣),例如我希望獲取一些blog的RSS來生成一些內容,再或者說我在“人人開放平臺”上開發一個應用,需要調用人人的數據。
然而,很不幸的是,直接用XMLHttpRequest來Get或者Post是不行的,例如我用jQuery的$.get去訪問本小博的主域名 :
$.get("http://flycoder.org/",
{}, function(data){
alert('跨域不是越獄:'+data)
}, "html");
結果如下(總之就是不行啦~FF不報錯,但是木有返回數據):
vc3MtZG9tYWlu” width=”364″ />
那咋麼辦捏?(弱弱的說,測試的時候我發現IE訪問本地文件時,是可以跨域的,不過這也沒啥用~囧~)
3、腫麼跨域
下面為瞭更好的講解和測試,我們可以通過修改hosts文件來模擬跨域的效果,hosts文件在C:\Windows\System32\drivers\etc 文件夾下。在下面加3行:
127.0.0.1 www.a.com
127.0.0.1 a.com
127.0.0.1 www.b.com
3.1、跨域代理
一種簡單的辦法,就是把跨域的工作交給服務器,從後臺獲取其他站點的數據再返回給前臺,也就是跨域代理(Cross Domain Proxy)。
這種方法似乎蠻簡單的,改動也不太大。不過就是http請求多瞭些,響應慢瞭些,服務器的負載重瞭些~
3.2、document.domain+iframe
對於主域相同而子域不同的例子,可以通過設置document.domain的辦法來解決。
舉www.a.com/a.html和a.com/b.html為例,隻需在a.html中添加一個b.html的iframe,並且設置兩個頁面的document.domain都為'a.com'(隻能為主域名),兩個頁面之間即可互相訪問瞭,代碼如下:
www.a.com/a.html中的script
document.domain='a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
//獲取iframe的document對象
//W3C的標準方法是iframe.contentDocument,
//IE6、7可以使用document.frames[ID].document
//為瞭更好兼容,可先獲取iframe的window對象iframe.contentWindow
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在這裡操縱b.html
alert(doc.getElementById("test").innerHTML);
};
a.com/b.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<script type="text/javascript">
document.domain='a.com';
</script>
</head>
<body>
<h1 id="test">Hello World</h1>
</body>
</html>
如果b.html要訪問a.html,可在子窗口(iframe)中通過window.parent來訪問父窗口的window對象,然後就可以為所欲為瞭(window對象都有瞭,還有啥不行的),同理子窗口也可以和子窗口之間通信。
於是,我們可以通過b.html的XMLHttpRequest來獲取數據,再傳給a.html,從而解決跨子域獲取數據的問題。
但是這種方法隻支持同一根域名下的頁面,如果不同根域名(例如baidu.com想訪問google.com)那就無能為力瞭。
3.3、動態script標簽(Dynamic Script Tag)
這種方法也叫“動態腳本註入”。詳情
這種技術克服瞭XMLHttpRequest的最大限制,也就是跨域請求數據。直接用JavaScript創建一個新的腳本標簽,然後設置它的src屬性為不同域的URL。
www.a.com/a.html中的script
var dynScript = document.createElement('script');
dynScript.src = 'http://www.b.com/b.js';
dynScript.setAttribute("type", "text/javascript");
document.getElementsByTagName('head')[0]
.appendChild(dynScript);
通過動態標簽註入的必須是可執行的JavaScript代碼,因此無論是你的數據格式是啥(xml、json等),都必須封裝在一個回調函數中。一個回調函數如下:
www.a.com/a.html中的script
function dynCallback(data){
//處理數據, 此處簡單示意一下
alert(data.content);
}
在這個例子中,www.b.com/b.js需要將數據封裝在上面這個dynCallback函數中,如下:
1
dynCallback({content:'Hello World'})
我們看到瞭讓人開心的結果,Hello World~
不過動態腳本註入還是存在不少問題的,下面我們拿它和XMLHttpRequest來對比一下:
XmlHttpRequest | Dynamic Script Tag | |
---|---|---|
跨瀏覽器兼容 | No | Yes |
跨域限制 | Yes | No |
接收HTTP狀態 | Yes | No (除瞭200) |
支持Get、Post | Yes |
No (GET only) |
發送、接收HTTP頭 | Yes | No |
接收XML | Yes | Yes |
接收JSON | Yes | Yes |
支持同步、異步 | Yes | No (隻能異步) |
可以看出,動態腳本註入還是有不少限制,隻能使用Get,不能像XHR一樣判斷Http狀態等。
而且使用動態腳本註入的時候必須註意安全問題。因為JavaScript沒有任何權限與訪問控制的概念,通過動態腳本註入的代碼可以完全控制整個頁面,所以引入外部來源的代碼必須多加小心
作者 jiangzhenghua