JSONP
jsonp其實是由兩部分組成的:回調(diào)函數(shù)和數(shù)據(jù)∧鳎回調(diào)函數(shù)是當(dāng)響應(yīng)到來時應(yīng)該在頁面中調(diào)用的函數(shù),而數(shù)據(jù)就是傳入回調(diào)函數(shù)中的json參數(shù)。利用的是script標(biāo)簽中src可以引用不同域上js文件的思想楚里,進行跨域。
- jsonp的優(yōu)點: 它不像XHR對象實現(xiàn)ajax請求那樣受到同源政策的限制猎贴,它的兼容性更好班缎,XHR在進行ajax請求時,為了兼容IE她渴,還需創(chuàng)建ActiveX對象达址。并且在請求完畢之后可以通過回調(diào)函數(shù)的方式將結(jié)果回傳
- jsonp的缺點: 它只支持get請求而不支持post等其它類型的HTTP請求;它只支持跨域HTTP請求這種情況趁耗,不能解決不同域的兩個頁面之間如何進行javascript調(diào)用問題
實驗代碼:
<!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>
服務(wù)器代碼:
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與JSONP對比:
CORS無疑更為先進,方便和可靠苛败。
1.jsonp只能實現(xiàn)get請求满葛,而CORS支持所有類型的HTTP請求
2.使用CORS,開發(fā)者可以使用普通的XHR發(fā)起請求和獲得數(shù)據(jù)罢屈,比起jsonp有更好的錯誤處理
3.jsonp主要被老的瀏覽器支持嘀韧,而絕大多數(shù)現(xiàn)代瀏覽器都已經(jīng)支持CORS
CORS可以分成兩種:
1、簡單請求
2缠捌、復(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)中填寫并以逗號進行分隔
如果僅僅是簡單請求,那么即便不用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)對這兩項進行回復(fù)署辉,以讓瀏覽器確定請求是否能夠成功完成。
復(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ā)送巍棱。
<!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>
//服務(wù)器
</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);
})
降域
頁面1:
<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>
頁面2:
<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>
postMessage
頁面1:
<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>
頁面2:
<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>