JSONP
利用 <script>標(biāo)簽沒有跨域限制的“漏洞”來達(dá)到與第三方通訊的目的。
當(dāng)需要通訊時姻报,本站腳本創(chuàng)建一個元素瞒滴,地址指向第三方的API網(wǎng)址鹤耍,
如:<script src="http://www.example.net/api?param1=1¶m2=2"></script>
并提供一個回調(diào)函數(shù)來接收數(shù)據(jù)(函數(shù)名可約定城豁,或通過地址參數(shù)傳遞)苟穆。 第三方產(chǎn)生的響應(yīng)為json數(shù)據(jù)的包裝(故稱之為jsonp,即json padding)
如:callback({"name":"hax","gender":"Male"}) 這樣瀏覽器會調(diào)用callback函數(shù)唱星,并傳遞解析后json對象作為參數(shù)雳旅。
優(yōu)點:簡單,老式瀏覽器全部支持间聊,服務(wù)器改造小攒盈。不需要XMLHttpRequest或ActiveX的支持。
缺點:只支持GET請求哎榴。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)</li>
<li>男雙力爭會師決賽 </li>
<li>女排將死磕巴西型豁!</li>
</ul>
<button class="change">換一組</button>
</div>
<!--<script src="http://localhost:8080/a.js"></script>-->
<script>
$('.change').addEventListener('click', function () {
var script=document.createElement('script');
script.src='http://localhost:8080/getNews?callback=appendHtml';
document.head.appendChild(script);
document.head.removeChild(script);
});
function appendHtml(news) {
var html ='';
for(var i=0;i<news.length;i++){
html+='<li>'+news[i]+'</li>';
}
console.log(html);
$('.news').innerHTML=html;
}
function $(id){
return document.querySelector(id);
}
</script>
</body>
</html>
后端
app.get('/getNews', function (req, res) {
var news = [
"1.三國",
"2.水滸",
"3.紅樓",
"4.西游",
"5.聊齋",
"6.山海",
"7.道德",
"8.三字"
]
var data = [];
for (var i = 0; i < 3; i++) {
var index = parseInt(Math.random() * news.length);
data.push(news[index]);
news.splice(index, 1);
}
var cb = req.query.callback;
if (cb) {
res.send(cb + '(' + JSON.stringify(data) + ')');
} else {
res.send(data);
}
})
CORS
CORS即Cross Origin Resource Sharing(跨來源資源共享),通俗說就是我們所熟知的跨域請求尚蝌。眾所周知偷遗,在以前,跨域可以采用代理驼壶、JSONP等方式,而在Modern瀏覽器面前喉酌,這些終將成為過去式热凹,因為有了CORS。
CORS在最初接觸的時候只大概了解到泪电,通過服務(wù)器端設(shè)置Access-Control-Allow-Origin響應(yīng)頭般妙,即可使指定來源像訪問同源接口一樣訪問跨域接口,最近在使用CORS的時候相速,由于需要傳輸自定義Header信息碟渺,發(fā)現(xiàn)原來CORS的規(guī)范定義遠(yuǎn)不止這些。
CORS可以分成簡單請求和復(fù)雜請求
一個簡單的請求大致如下:
HTTP方法是下列之一:
HEAD
GET
POST
HTTP頭包含:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type突诬,但僅能是下列之一
application/x-www-form-urlencoded
multipart/form-data
text/plain
任何一個不滿足上述要求的請求苫拍,即被認(rèn)為是復(fù)雜請求。一個復(fù)雜請求不僅有包含通信內(nèi)容的請求旺隙,同時也包含預(yù)請求(preflight request)绒极。
簡單請求的發(fā)送從代碼上來看和普通的XHR沒太大區(qū)別,但是HTTP頭當(dāng)中要求總是包含一個域(Origin)的信息蔬捷。該域包含協(xié)議名垄提、地址以及一個可選的端口榔袋。不過這一項實際上由瀏覽器代為發(fā)送,并不是開發(fā)者代碼可以觸及到的铡俐。
簡單請求的部分響應(yīng)頭及解釋如下:
Access-Control-Allow-Origin(必含)- 不可省略凰兑,否則請求按失敗處理。該項控制數(shù)據(jù)的可見范圍审丘,如果希望數(shù)據(jù)對任何人都可見吏够,可以填寫"*"。
Access-Control-Allow-Credentials(可選) – 該項標(biāo)志著請求當(dāng)中是否包含cookies信息备恤,只有一個可選值:true(必為小寫)稿饰。如果不包含cookies,請略去該項露泊,而不是填寫false喉镰。這一項與XmlHttpRequest2對象當(dāng)中的withCredentials屬性應(yīng)保持一致,即withCredentials為true時該項也為true惭笑;withCredentials為false時侣姆,省略該項不寫。反之則導(dǎo)致請求失敗沉噩。
Access-Control-Expose-Headers
(可選) – 該項確定XmlHttpRequest2對象當(dāng)中g(shù)etResponseHeader()方法所能獲得的額外信息捺宗。通常情況下,getResponseHeader()方法只能獲得如下的信息:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
當(dāng)你需要訪問額外的信息時川蒙,就需要在這一項當(dāng)中填寫并以逗號進(jìn)行分隔
如果僅僅是簡單請求蚜厉,那么即便不用CORS也沒有什么大不了,但CORS的復(fù)雜請求就令CORS顯得更加有用了畜眨。簡單來說昼牛,任何不滿足上述簡單請求要求的請求,都屬于復(fù)雜請求康聂。比如說你需要發(fā)送PUT贰健、DELETE等HTTP動作,或者發(fā)送Content-Type: application/json的內(nèi)容恬汁。
復(fù)雜請求表面上看起來和簡單請求使用上差不多伶椿,但實際上瀏覽器發(fā)送了不止一個請求。其中最先發(fā)送的是一種"預(yù)請求"氓侧,此時作為服務(wù)端脊另,也需要返回"預(yù)回應(yīng)"作為響應(yīng)。預(yù)請求實際上是對服務(wù)端的一種權(quán)限請求约巷,只有當(dāng)預(yù)請求成功返回尝蠕,實際請求才開始執(zhí)行。
預(yù)請求以O(shè)PTIONS形式發(fā)送载庭,當(dāng)中同樣包含域看彼,并且還包含了兩項CORS特有的內(nèi)容:
Access-Control-Request-Method – 該項內(nèi)容是實際請求的種類廊佩,可以是GET、POST之類的簡單請求靖榕,也可以是PUT标锄、DELETE等等。
Access-Control-Request-Headers – 該項是一個以逗號分隔的列表茁计,當(dāng)中是復(fù)雜請求所使用的頭部料皇。
顯而易見,這個預(yù)請求實際上就是在為之后的實際請求發(fā)送一個權(quán)限請求星压,在預(yù)回應(yīng)返回的內(nèi)容當(dāng)中践剂,服務(wù)端應(yīng)當(dāng)對這兩項進(jìn)行回復(fù),以讓瀏覽器 ## 標(biāo)題頭
確定請求是否能夠成功完成娜膘。
復(fù)雜請求的部分響應(yīng)頭及解釋如下:
Access-Control-Allow-Origin(必含) – 和簡單請求一樣的逊脯,必須包含一個域。
Access-Control-Allow-Methods(必含) – 這是對預(yù)請求當(dāng)中Access-Control-Request-Method的回復(fù)竣贪,這一回復(fù)將是一個以逗號分隔的列表军洼。盡管客戶端或許只請求某一方法,但服務(wù)端仍然可以返回所有允許的方法演怎,以便客戶端將其緩存匕争。
Access-Control-Allow-Headers(當(dāng)預(yù)請求中包含Access-Control-Request-Headers時必須包含) – 這是對預(yù)請求當(dāng)中Access-Control-Request-Headers的回復(fù),和上面一樣是以逗號分隔的列表爷耀,可以返回所有支持的頭部甘桑。這里在實際使用中有遇到,所有支持的頭部一時可能不能完全寫出來歹叮,而又不想在這一層做過多的判斷跑杭,沒關(guān)系,事實上通過request的header可以直接取到Access-Control-Request-Headers盗胀,直接把對應(yīng)的value設(shè)置到Access-Control-Allow-Headers即可。
Access-Control-Allow-Credentials(可選) – 和簡單請求當(dāng)中作用相同锄贼。
Access-Control-Max-Age(可選) – 以秒為單位的緩存時間票灰。預(yù)請求的的發(fā)送并非免費午餐,允許時應(yīng)當(dāng)盡可能緩存宅荤。
一旦預(yù)回應(yīng)如期而至屑迂,所請求的權(quán)限也都已滿足,則實際請求開始發(fā)送冯键。
通caniuse.com得知惹盼,目前大部分Modern瀏覽器已經(jīng)支持完整的CORS,但I(xiàn)E直到IE11才完美支持惫确,所以對于PC網(wǎng)站手报,還是建議采用其他解決方案蚯舱,如果僅僅是移動端網(wǎng)站,大可放心使用掩蛤。
代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)</li>
<li>男雙力爭會師決賽 </li>
<li>女排將死磕巴西枉昏!</li>
</ul>
<button class="change">換一組</button>
</div>
<script>
$('.change').addEventListener('click', function(){
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://a.jirengu:8080/getNews', true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
appendHtml( JSON.parse(xhr.responseText) )
}
}
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id){
return document.querySelector(id);
}
</script>
</body>
后端
</html>app.get('/getNews', function (req, res) {
var news = [
"1.三國",
"2.水滸",
"3.紅樓",
"4.西游",
"5.聊齋",
"6.山海",
"7.道德",
"8.三字"
]
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://c.jrg.com:8080");
res.header("Access-Control-Allow-Origin", "*");
res.send(data);
})
降域 修改document.domain
A頁面
<html>
<style>
.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>測試</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
//URL: http://a.jrg.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 = "jrg.com";
</script>
</html>
B頁面
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
// URL: http://b.jrg.com:8080/b.html
document.querySelector('#input').addEventListener('input', function(){
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'jrg.com';
</script>
</html>
window.postMessage方法
頁面A
<style>
.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>跨域postMessage測試</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
$('.main input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
頁面B
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
// URL: http://b.jrg.com:8080/b.html
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');
})
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
</html>