1.什么是同源策略
瀏覽器出于安全方面的考慮暂幼,只允許與本域下的接口交互。不同源的客戶端腳本在沒(méi)有明確授權(quán)的情況下,不能讀寫對(duì)方的資源核芽。
- 同協(xié)議:如都是http或者h(yuǎn)ttps
- 同域名:如都是http://jirengu.com/a 和-http://jirengu.com/b
- 同端口:如都是80端口
需要注意的是: 對(duì)于當(dāng)前頁(yè)面來(lái)說(shuō)頁(yè)面存放的 JS 文件的域不重要妖谴,重要的是加載該 JS 頁(yè)面所在什么域
2.
什么是跨域
跨域是指從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源窿锉。嚴(yán)格的定義是:只要不符合同源政策的任意一條,就被當(dāng)作跨域膝舅。
為什么瀏覽器要限制跨域
原因就是安全問(wèn)題:如果一個(gè)網(wǎng)頁(yè)可以隨意的訪問(wèn)另外一個(gè)網(wǎng)站的資源嗡载,那么就有可能在客戶完全不知情的情況下出現(xiàn)安全問(wèn)題。
跨域的實(shí)現(xiàn)形式
同源政策做的限制:
(1) Cookie仍稀、LocalStorage 和 IndexDB 無(wú)法讀取洼滚。
(2) DOM 無(wú)法獲得。
(3) AJAX 請(qǐng)求不能發(fā)送技潘。
關(guān)于Cookie:
兩個(gè)網(wǎng)頁(yè)一級(jí)域名相同遥巴,只是二級(jí)域名不同千康,瀏覽器允許通過(guò)設(shè)置document.domain共享 Cookie。
A網(wǎng)頁(yè)通過(guò)腳本設(shè)置一個(gè)cookie
document.cookie = "test1=hello";
B網(wǎng)頁(yè)就可以讀到這個(gè) Cookie挪哄。
var allCookie = document.cookie;
注意吧秕,這種方法只適用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 無(wú)法通過(guò)這種方法,規(guī)避同源政策迹炼,而要使用下文介紹的PostMessage API砸彬。
服務(wù)器也可以在設(shè)置Cookie的時(shí)候,指定Cookie的所屬域名為一級(jí)域名
Set-Cookie: key=value; domain=.example.com; path=/
關(guān)于iframe
如果兩個(gè)網(wǎng)頁(yè)不同源斯入,就無(wú)法拿到對(duì)方的DOM砂碉。典型的例子是iframe窗口和window.open方法打開的窗口,它們與父窗口無(wú)法通信刻两。
document.getElementById("myIFrame").contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.
上面命令中增蹭,父窗口想獲取子窗口的DOM,因?yàn)榭缭磳?dǎo)致報(bào)錯(cuò)磅摹。
反之亦然滋迈,子窗口獲取主窗口的DOM也會(huì)報(bào)錯(cuò)。
window.parent.document.body
// 報(bào)錯(cuò)
對(duì)于完全不同源的網(wǎng)站户誓,目前有三種方法饼灿,可以解決跨域窗口的通信問(wèn)題。
1.片段識(shí)別符(fragment identifier)
2.window.name
3.跨文檔通信API(Cross-document messaging)
破解方法:
1.片段識(shí)別符
片段標(biāo)識(shí)符(fragment identifier)指的是帝美,URL的#號(hào)后面的部分碍彭,比如http://example.com/x.html#fragment的#fragment。如果只是改變片段標(biāo)識(shí)符悼潭,頁(yè)面不會(huì)重新刷新庇忌。
父窗口可以把信息,寫入子窗口的片段標(biāo)識(shí)符舰褪。
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;
子窗口通過(guò)監(jiān)聽hashchange事件得到通知皆疹。
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
// ...
}
同樣的,子窗口也可以改變父窗口的片段標(biāo)識(shí)符占拍。
parent.location.href= target + "#" + hash;
2.window.name
瀏覽器窗口有window.name屬性墙基。這個(gè)屬性的最大特點(diǎn)是,無(wú)論是否同源刷喜,只要在同一個(gè)窗口里残制,前一個(gè)網(wǎng)頁(yè)設(shè)置了這個(gè)屬性,后一個(gè)網(wǎng)頁(yè)可以讀取它掖疮。
父窗口先打開一個(gè)子窗口初茶,載入一個(gè)不同源的網(wǎng)頁(yè),該網(wǎng)頁(yè)將信息寫入window.name屬性。
window.name = data;
接著恼布,子窗口跳回一個(gè)與主窗口同域的網(wǎng)址螺戳。
location = '[http://parent.url.com/xxx.html](http://parent.url.com/xxx.html)';
主窗口就可以讀取子窗口的window.name
var data = document.getElementById('myFrame').contentWindow.name;
這種方法的優(yōu)點(diǎn)是,window.name容量很大折汞,可以放置非常長(zhǎng)的字符串倔幼;缺點(diǎn)是必須監(jiān)聽子窗口window.name屬性的變化,影響網(wǎng)頁(yè)性能爽待。
3.window.postMessage
允許跨窗口通信损同,不論這兩個(gè)窗口是否同源。
舉例來(lái)說(shuō)鸟款,父窗口http://aaa.com向子窗口http://bbb.com發(fā)消息膏燃,調(diào)用postMessage方法就可以了。
var popup = window.open('[http://bbb.com](http://bbb.com/)', 'title');
popup.postMessage('Hello World!', '[http://bbb.com](http://bbb.com/)');
postMessage方法的第一個(gè)參數(shù)是具體的信息內(nèi)容何什,第二個(gè)參數(shù)是接收消息的窗口的源(origin)组哩,即"協(xié)議 + 域名 + 端口"。也可以設(shè)為*处渣,表示不限制域名伶贰,向所有窗口發(fā)送。
子窗口向父窗口發(fā)送消息
window.opener.postMessage('Nice to see you', '[http://aaa.com](http://aaa.com/)');
父窗口和子窗口都可以通過(guò)message事件罐栈,監(jiān)聽對(duì)方的消息黍衙。
window.addEventListener('message', function(e) {
console.log(e.data);
},false);
message事件的事件對(duì)象event,提供以下三個(gè)屬性悠瞬。
event.source:發(fā)送消息的窗口
event.origin: 消息發(fā)向的網(wǎng)址
event.data: 消息內(nèi)容
下面的例子是们豌,子窗口通過(guò)event.source屬性引用父窗口涯捻,然后發(fā)送消息浅妆。
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*');
}
event.origin屬性可以過(guò)濾不是發(fā)給本窗口的消息。
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
if (event.origin !== '[http://aaa.com](http://aaa.com/)') return;
if (event.data === 'Hello World') { event.source.postMessage('Hello', event.origin);
} else { console.log(event.data);
}
}
4.LocalStorage
通過(guò)window.postMessage障癌,讀寫其他窗口的 LocalStorage 也成為了可能凌外。
下面是一個(gè)例子,主窗口寫入iframe子窗口的localStorage涛浙。
window.onmessage = function(e) {
if (e.origin !== '[http://bbb.com](http://bbb.com/)') { return; }
var payload = JSON.parse(e.data);
localStorage.setItem(payload.key,
JSON.stringify(payload.data));
};
上面代碼中康辑,子窗口將父窗口發(fā)來(lái)的消息,寫入自己的LocalStorage轿亮。
父窗口發(fā)送消息的代碼如下疮薇。
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack};
win.postMessage(JSON.stringify({key: 'storage', data: obj}),
'[http://bbb.com](http://bbb.com/)');
加強(qiáng)版的子窗口接收消息的代碼如下。
window.onmessage = function(e) {
if (e.origin !== '[http://bbb.com](http://bbb.com/)') return;
var payload = JSON.parse(e.data);
switch (payload.method) {
case 'set':
localStorage.setItem(payload.key, JSON.stringify(payload.data));
break;
case 'get':
var parent = window.parent;
var data = localStorage.getItem(payload.key);
parent.postMessage(data, '[http://aaa.com](http://aaa.com/)');
break;
case 'remove':
localStorage.removeItem(payload.key);
break; }
};
加強(qiáng)版的父窗口發(fā)送消息代碼如下我注。
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
// 存入對(duì)象
win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), '[http://bbb.com](http://bbb.com/)');
// 讀取對(duì)象
win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
window.onmessage = function(e) {
if (e.origin != '[http://aaa.com](http://aaa.com/)') return;
// "Jack"
console.log(JSON.parse(e.data).name);};
3.JSONP 的原理是什么
JSONP是服務(wù)器與客戶端跨源通信的常用方法按咒。最大特點(diǎn)就是簡(jiǎn)單適用,老式瀏覽器全部支持但骨,服務(wù)器改造非常小励七。
它的基本思想是智袭,網(wǎng)頁(yè)通過(guò)添加一個(gè)<script>元素,向服務(wù)器請(qǐng)求JSON數(shù)據(jù)掠抬,這種做法不受同源政策限制吼野;服務(wù)器收到請(qǐng)求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)里傳回來(lái)两波。不過(guò)它只能發(fā)get請(qǐng)求瞳步。
首先,網(wǎng)頁(yè)動(dòng)態(tài)插入<script>元素雨女,由它向跨源網(wǎng)址發(fā)出請(qǐng)求谚攒。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src; document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('[http://example.com/ip](http://example.com/ip)?callback=foo');
//回調(diào)函數(shù)callback
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
上面代碼通過(guò)動(dòng)態(tài)添加<script>元素,向服務(wù)器example.com發(fā)出請(qǐng)求氛堕。注意馏臭,該請(qǐng)求的查詢字符串有一個(gè)callback參數(shù),用來(lái)指定回調(diào)函數(shù)的名字讼稚,這對(duì)于JSONP是必需的括儒。
服務(wù)器收到這個(gè)請(qǐng)求以后,會(huì)將數(shù)據(jù)放在回調(diào)函數(shù)的參數(shù)位置返回锐想。
foo({
"ip": "8.8.8.8"
});
由于<script>元素請(qǐng)求的腳本帮寻,直接作為代碼運(yùn)行。這時(shí)赠摇,只要瀏覽器定義了foo函數(shù)固逗,該函數(shù)就會(huì)立即調(diào)用。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對(duì)象藕帜,而不是字符串烫罩,因此避免了使用JSON.parse的步驟。
4.CORS是什么
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫洽故。它是W3C標(biāo)準(zhǔn)贝攒,是跨源AJAX請(qǐng)求的根本解決方法。相比JSONP只能發(fā)GET請(qǐng)求时甚,CORS允許任何類型的請(qǐng)求隘弊。
CORS需要瀏覽器和服務(wù)器同時(shí)支持。目前荒适,所有瀏覽器都支持該功能梨熙,IE瀏覽器不能低于IE10。
對(duì)于開發(fā)者來(lái)說(shuō)刀诬,CORS通信與同源的AJAX通信沒(méi)有差別咽扇,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息肌割,有時(shí)還會(huì)多出一次附加的請(qǐng)求卧蜓,但用戶不會(huì)有感覺。
因此把敞,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器弥奸。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信奋早。
瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)
只要同時(shí)滿足以下兩大條件盛霎,就屬于簡(jiǎn)單請(qǐng)求。
(1) 請(qǐng)求方法是以下三種方法之一:
HEAD
GET
POST
(2)HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三個(gè)值application/x-www-form-urlencoded耽装、
multipart/form-data愤炸、text/plain
凡是不同時(shí)滿足上面兩個(gè)條件,就屬于非簡(jiǎn)單請(qǐng)求掉奄。
瀏覽器對(duì)這兩種請(qǐng)求的處理规个,是不一樣的。
對(duì)于簡(jiǎn)單請(qǐng)求姓建,瀏覽器直接發(fā)出CORS請(qǐng)求
就是在頭信息之中诞仓,增加一個(gè)Origin字段
GET /cors HTTP/1.1
Origin: [http://api.bob.com](http://api.bob.com/)
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
上面的頭信息中,Origin字段用來(lái)說(shuō)明速兔,本次請(qǐng)求來(lái)自哪個(gè)源(協(xié)議 + 域名 + 端口)墅拭。服務(wù)器根據(jù)這個(gè)值,決定是否同意這次請(qǐng)求涣狗。
如果Origin指定的源谍婉,不在許可范圍內(nèi),服務(wù)器會(huì)返回一個(gè)正常的HTTP回應(yīng)镀钓。瀏覽器發(fā)現(xiàn)穗熬,這個(gè)回應(yīng)的頭信息沒(méi)有包含Access-Control-Allow-Origin字段(詳見下文),就知道出錯(cuò)了掸宛,從而拋出一個(gè)錯(cuò)誤死陆,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲招拙。注意唧瘾,這種錯(cuò)誤無(wú)法通過(guò)狀態(tài)碼識(shí)別,因?yàn)镠TTP回應(yīng)的狀態(tài)碼有可能是200别凤。
如果Origin指定的域名在許可范圍內(nèi)饰序,服務(wù)器返回的響應(yīng),會(huì)多出幾個(gè)頭信息字段规哪。
//該字段必須求豫,值為origin或者"*"
Access-Control-Allow-Origin: [http://api.bob.com](http://api.bob.com/)
/*
該字段可選,表示是否允許發(fā)送Cookie,該值默認(rèn)為true,
如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可蝠嘉。
*/
Access-Control-Allow-Credentials: true
//該字段可選最疆,如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
上面的頭信息之中蚤告,有三個(gè)與CORS請(qǐng)求相關(guān)的字段努酸,都以Access-Control-開頭。
非簡(jiǎn)單請(qǐng)求
1.預(yù)檢請(qǐng)求
非簡(jiǎn)單請(qǐng)求是那種對(duì)服務(wù)器有特殊要求的請(qǐng)求杜恰,比如請(qǐng)求方法是PUT或DELETE获诈,或者Content-Type字段的類型是application/json。
var url = '[http://api.alice.com/cors](http://api.alice.com/cors)';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代碼中心褐,HTTP請(qǐng)求的方法是PUT舔涎,并且發(fā)送一個(gè)自定義頭信息X-Custom-Header。
瀏覽器發(fā)現(xiàn)逗爹,這是一個(gè)非簡(jiǎn)單請(qǐng)求亡嫌,就自動(dòng)發(fā)出一個(gè)"預(yù)檢"請(qǐng)求,要求服務(wù)器確認(rèn)可以這樣請(qǐng)求掘而。下面是這個(gè)"預(yù)檢"請(qǐng)求的HTTP頭信息昼伴。
OPTIONS /cors HTTP/1.1
Origin: [http://api.bob.com](http://api.bob.com/)
Access-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-Header
Host: api.alice.comAccept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"預(yù)檢"請(qǐng)求用的請(qǐng)求方法是OPTIONS,表示這個(gè)請(qǐng)求是用來(lái)詢問(wèn)的镣屹。頭信息里面署尤,關(guān)鍵字段是Origin,表示請(qǐng)求來(lái)自哪個(gè)源炮赦。
2.預(yù)檢請(qǐng)求的回應(yīng)
服務(wù)器收到"預(yù)檢"請(qǐng)求以后板驳,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后伪窖,確認(rèn)允許跨源請(qǐng)求逸寓,就可以做出回應(yīng)。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: [http://api.bob.com](http://api.bob.com/)
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的HTTP回應(yīng)中覆山,關(guān)鍵的是Access-Control-Allow-Origin字段竹伸,表示http://api.bob.com可以請(qǐng)求數(shù)據(jù)。該字段也可以設(shè)為星號(hào)簇宽,表示同意任意跨源請(qǐng)求勋篓。
Access-Control-Allow-Origin: *
如果瀏覽器否定了"預(yù)檢"請(qǐng)求,會(huì)返回一個(gè)正常的HTTP回應(yīng)魏割,但是沒(méi)有任何CORS相關(guān)的頭信息字段譬嚣。這時(shí),瀏覽器就會(huì)認(rèn)定钞它,服務(wù)器不同意預(yù)檢請(qǐng)求拜银,因此觸發(fā)一個(gè)錯(cuò)誤殊鞭,被XMLHttpRequest對(duì)象的onerror回調(diào)函數(shù)捕獲∧嵬埃控制臺(tái)會(huì)打印出如下的報(bào)錯(cuò)信息操灿。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
3.瀏覽器的正常請(qǐng)求和回應(yīng)
一旦服務(wù)器通過(guò)了"預(yù)檢"請(qǐng)求,以后每次瀏覽器正常的CORS請(qǐng)求泵督,就都跟簡(jiǎn)單請(qǐng)求一樣牲尺,會(huì)有一個(gè)Origin頭信息字段。服務(wù)器的回應(yīng)幌蚊,也都會(huì)有一個(gè)Access-Control-Allow-Origin頭信息字段谤碳。
下面是"預(yù)檢"請(qǐng)求之后,瀏覽器的正常CORS請(qǐng)求溢豆。
PUT /cors HTTP/1.1
Origin: [http://api.bob.com](http://api.bob.com/)
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
上面頭信息的Origin字段是瀏覽器自動(dòng)添加的蜒简。
下面是服務(wù)器正常的回應(yīng)。
Access-Control-Allow-Origin: [http://api.bob.com](http://api.bob.com/)
Content-Type: text/html; charset=utf-8
上面頭信息中漩仙,Access-Control-Allow-Origin字段是每次回應(yīng)都必定包含的搓茬。
- 與JSONP的比較
CORS與JSONP的使用目的相同,但是比JSONP更強(qiáng)大队他。
JSONP只支持GET請(qǐng)求卷仑,CORS支持所有類型的HTTP請(qǐng)求。JSONP的優(yōu)勢(shì)在于支持老式瀏覽器麸折,以及可以向不支持CORS的網(wǎng)站請(qǐng)求數(shù)據(jù)锡凝。
5.根據(jù)視頻里的講解演示三種以上跨域的解決方式
JSONP:
<!DOCTYPE html>
<html>
<head>
<title>JSONP</title>
<style type="text/css">
ul,li{
list-style: none;
}
li:hover{
color: #fff;
background-color: #6ff;
}
.news{
margin: auto;
}
li{
margin: 10px ;
}
.btn {
outline: none;
border: 1px solid #ddd;
color: #000;
background-color: #fff;
border-radius: 3px;
display: block;
margin: 15px 50px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="ct">
<ul class="news">
<li>第11日前瞻,中國(guó)沖擊4金垢啼,博爾特再戰(zhàn)</li>
<li>男雙力爭(zhēng)會(huì)師決賽</li>
<li>女排將死磕巴西窜锯!</li>
</ul>
<button class="btn">換一組</button>
</div>
<script type="text/javascript">
function $(e){
return document.querySelector(e);
}
$('.btn').addEventListener('click',function(){
var script = document.createElement('script')
script.src = 'http://a.hgz.com:8080/getNews?callback=foo'
document.head.appendChild(script)
document.head.removeChild(script)
})
function foo(news){
var html = ''
for(var i =0; i<news.length;i++){
html+= '<li>' + news[i] + '</li>'
}
$('.news').innerHTML = html
}
</script>
</body>
</html>
router.js
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻,中國(guó)沖擊4金芭析,博爾特再戰(zhàn)",
"男雙力爭(zhēng)會(huì)師決賽",
"女排將死磕巴西锚扎!郎平安排男陪練模仿對(duì)方核心",
"沒(méi)有中國(guó)選手和巨星的110米欄 我們還看嗎",
"中英上演奧運(yùn)金牌大戰(zhàn)",
"博彩賠率挺中國(guó)奪回第二紐約時(shí)報(bào):中國(guó)因?qū)κ址幎鴣G失的獎(jiǎng)牌最多",
"[邊界] 最“出柜”奧運(yùn) 同性之愛閃耀里約",
"[特評(píng)] 下跪拜謝與洪荒之力一樣 都是真情流露"
]
var data = []
for(var i = 0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index])
news.splice(index, 1) //保證3條信息不重復(fù)
}
var cb = req.query.callback; //必須重新聲明callback
res.send(cb + '(' + JSON.stringify(data) + ')'); //生成foo(news)的形式的字符串
})
CORS
代碼與ajax幾乎一樣
<!DOCTYPE html>
<html>
<head>
<title>JSONP</title>
<style type="text/css">
ul,li{
list-style: none;
}
li:hover{
color: #fff;
background-color: #6ff;
}
.news{
margin: auto;
}
li{
margin: 10px ;
}
.btn {
outline: none;
border: 1px solid #ddd;
color: #000;
background-color: #fff;
border-radius: 3px;
display: block;
margin: 15px 50px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="ct">
<ul class="news">
<li>第11日前瞻,中國(guó)沖擊4金馁启,博爾特再戰(zhàn)</li>
<li>男雙力爭(zhēng)會(huì)師決賽</li>
<li>女排將死磕巴西驾孔!</li>
</ul>
<button class="btn">換一組</button>
</div>
<script type="text/javascript">
function $(e){
return document.querySelector(e);
}
$('.btn').addEventListener('click',function(){
var xhr = new XMLHttpRequest()
xhr.open('get', 'http://hgz.com:8080/getNews', true)
xhr.send()
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status ===200 || xhr.status=== 304){
foo(JSON.parse(xhr.responseText))
}
}
}
})
function foo(news){
var html = ''
for(var i =0; i<news.length;i++){
html+= '<li>' + news[i] + '</li>'
}
$('.news').innerHTML = html
}
</script>
</body>
</html>
router.js
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻,中國(guó)沖擊4金惯疙,博爾特再戰(zhàn)",
"男雙力爭(zhēng)會(huì)師決賽",
"女排將死磕巴西翠勉!郎平安排男陪練模仿對(duì)方核心",
"沒(méi)有中國(guó)選手和巨星的110米欄 我們還看嗎",
"中英上演奧運(yùn)金牌大戰(zhàn)",
"博彩賠率挺中國(guó)奪回第二紐約時(shí)報(bào):中國(guó)因?qū)κ址幎鴣G失的獎(jiǎng)牌最多",
"[邊界] 最“出柜”奧運(yùn) 同性之愛閃耀里約",
"[特評(píng)] 下跪拜謝與洪荒之力一樣 都是真情流露"
]
var data = []
for(var i = 0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index])
news.splice(index, 1)
}
res.header("Access-Control-Allow-Origin", "http://hgz.com:8080") //返回的res頭部加上Origin
// res.header("Access-Control-Allow-Origin", "*")
res.send(data);
})
演示
降域
a.html
<!DOCTYPE html>
<html>
<title></title>
<style type="text/css">
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用降域?qū)崿F(xiàn)跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.hgz.com:8080/a.html">
</div>
<iframe src="http://a.hgz.com:8080/b.html" frameborder="0"></iframe>
</div>
<script type="text/javascript">
//URL:http//a.hgz.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value)
window.frames[0].document.querySelector('input').value = this.value
})
document.domain = 'hgz.com'
</script>
</html>
b.html
<!DOCTYPE html>
<html>
<style type="text/css">
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.hgz.com:8080/b.html">
<script>
// URL:http://b.hgz.com:8080/b.html
document.querySelector('#input').addEventListener('input',function(){
window.parent.document.querySelector('input').value = this.value
})
document.domain = 'hgz.com';
</script>
</html>
postMessage
a.html
<!DOCTYPE html>
<html>
<title></title>
<style type="text/css">
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用降域?qū)崿F(xiàn)跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.hgz.com:8080/a.html">
</div>
<iframe src="http://b.hgz.com:8080/b.html" frameborder="0"></iframe>
</div>
<script type="text/javascript">
//URL:http//a.hgz.com:8080/a.html
function $(e){
return document.querySelector(e)
}
$('.main input').addEventListener('input', function(){
console.log(this.value)
window.frames[0].postMessage(this.value, '*')
}) //左邊為消息,右邊同意任意跨源請(qǐng)求
window.addEventListener('message', function(e){
$('.main input').value = e.data
})
</script>
</html>
b.html
<!DOCTYPE html>
<html>
<style type="text/css">
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.hgz.com:8080/b.html">
<script>
// URL:http://b.hgz.com:8080/b.html
function $(e){
return document.querySelector(e)
}
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*')
})
window.addEventListener('message', function(e){
$('#input').value = e.data
console.log(e.data)
})
</script>
</html>
演示
文章摘錄自阮一峰的網(wǎng)絡(luò)日志瀏覽器同源政策及其規(guī)避方法和跨域資源共享 CORS 詳解