同源策略(Same origin Policy)
瀏覽器出于安全方面的考慮,只允許與本域下的接口交互比驻。不同源的客戶端腳本在沒有明確授權(quán)的情況下檀咙,不能讀寫對(duì)方的資源。
本域指的是嫁佳?
- 同協(xié)議:如都是http或者h(yuǎn)ttps
- 同域名:如都是http://jirengu.com/a 和http://jirengu.com/b
- 同端口:如都是80端口
如:
不同源的例子:
- http://jirengu.com/main.js 和 https://jirengu.com/a.php (協(xié)議不同)
- http://jirengu.com/main.js 和 http://bbs.jirengu.com/a.php (域名不同挨队,域名必須完全相同才可以)
- http://jiengu.com/main.js 和 http://jirengu.com:8080/a.php (端口不同,第一個(gè)是80)
需要注意的是: 對(duì)于當(dāng)前頁面來說頁面存放的 JS 文件的域不重要,重要的是加載該 JS 頁面所在什么域
注:請(qǐng)求是發(fā)送成功也接收成功了的蒿往,但是被瀏覽器給阻止了盛垦,是瀏覽器的機(jī)制
跨域的幾種方法
JSONP
HTML 中 script 標(biāo)簽可以加載其他域下的js,比如我們經(jīng)常引入一個(gè)其他域下線上cdn的jQuery瓤漏。那如何利用這個(gè)特性實(shí)現(xiàn)從其他域下獲取數(shù)據(jù)呢腾夯?
可以先這樣試試:
<script src="http://api.jirengu.com/weather.php"></script>
這時(shí)候會(huì)向天氣接口發(fā)送請(qǐng)求獲取數(shù)據(jù),獲取數(shù)據(jù)后做為 js 來執(zhí)行蔬充。 但這里有個(gè)問題蝶俱, 數(shù)據(jù)是 JSON 格式的數(shù)據(jù),直接作為 JS 運(yùn)行的話我如何去得到這個(gè)數(shù)據(jù)來操作呢饥漫?
這樣試試:
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>
這個(gè)請(qǐng)求到達(dá)后端后榨呆,后端會(huì)去解析callback這個(gè)參數(shù)獲取到字符串showData,在發(fā)送數(shù)據(jù)做如下處理:
之前后端返回?cái)?shù)據(jù): {"city": "hangzhou", "weather": "晴天"} 現(xiàn)在后端返回?cái)?shù)據(jù): showData({"city": "hangzhou", "weather": "晴天"}) 前端script標(biāo)簽在加載數(shù)據(jù)后會(huì)把 「showData({“city”: “hangzhou”, “weather”: “晴天”})」做為 js 來執(zhí)行庸队,這實(shí)際上就是調(diào)用showData這個(gè)函數(shù)积蜻,同時(shí)參數(shù)是 {“city”: “hangzhou”, “weather”: “晴天”}。 用戶只需要在加載提前在頁面定義好showData這個(gè)全局函數(shù)彻消,在函數(shù)內(nèi)部處理參數(shù)即可竿拆。
<script>
function showData(ret){
console.log(ret);
}
</script>
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>
這就是 JSONP(JSON with padding),總結(jié)一下:
JSONP是通過 script 標(biāo)簽加載數(shù)據(jù)的方式去獲取數(shù)據(jù)當(dāng)做 JS 代碼來執(zhí)行 提前在頁面上聲明一個(gè)函數(shù)证膨,函數(shù)名通過接口傳參的方式傳給后臺(tái)如输,后臺(tái)解析到函數(shù)名后在原始數(shù)據(jù)上「包裹」這個(gè)函數(shù)名,發(fā)送給前端。換句話說不见,JSONP 需要對(duì)應(yīng)接口的后端的配合才能實(shí)現(xiàn)澳化。
server.js
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')
http.createServer(function(req, res){
var pathObj = url.parse(req.url, true)
switch (pathObj.pathname) {
case '/getNews':
var news = [
"第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
"正直播柴飚/洪煒出戰(zhàn) 男雙力爭會(huì)師決賽",
"女排將死磕巴西!郎平安排男陪練模仿對(duì)方核心"
]
res.setHeader('Content-Type','text/json; charset=utf-8')
if(pathObj.query.callback){
res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')
}else{
res.end(JSON.stringify(news))
}
break;
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
if(e){
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
}else{
res.end(data)
}
})
}
}).listen(8080)
index.html
<!DOCTYPE html>
<html>
<body>
<div class="container">
<ul class="news">
</ul>
<button class="show">show news</button>
</div>
<script>
$('.show').addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://127.0.0.1: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>
</html>
CORS
CORS 全稱是跨域資源共享(Cross-Origin Resource Sharing)稳吮,是一種 ajax 跨域請(qǐng)求資源的方式缎谷,支持現(xiàn)代瀏覽器,IE支持10以上灶似。 實(shí)現(xiàn)方式很簡單列林,當(dāng)你使用 XMLHttpRequest 發(fā)送請(qǐng)求時(shí),瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略酪惭,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin希痴,后臺(tái)進(jìn)行一系列處理,如果確定接受請(qǐng)求則在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin; 瀏覽器判斷該相應(yīng)頭中是否包含 Origin 的值春感,如果有則瀏覽器會(huì)處理響應(yīng)砌创,我們就可以拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回鲫懒,這時(shí)我們無法拿到響應(yīng)數(shù)據(jù)嫩实。所以 CORS 的表象是讓你覺得它與同源的 ajax 請(qǐng)求沒啥區(qū)別,代碼完全一樣窥岩。
server.js
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')
http.createServer(function(req, res){
var pathObj = url.parse(req.url, true)
switch (pathObj.pathname) {
case '/getNews':
var news = [
"第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
"正直播柴飚/洪煒出戰(zhàn) 男雙力爭會(huì)師決賽",
"女排將死磕巴西甲献!郎平安排男陪練模仿對(duì)方核心"
]
res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
//res.setHeader('Access-Control-Allow-Origin','*')
res.end(JSON.stringify(news))
break;
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
if(e){
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
}else{
res.end(data)
}
})
}
}).listen(8080)
index.html
<!DOCTYPE html>
<html>
<body>
<div class="container">
<ul class="news">
</ul>
<button class="show">show news</button>
</div>
<script>
$('.show').addEventListener('click', function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:8080/getNews', true)
xhr.send()
xhr.onload = function(){
appendHtml(JSON.parse(xhr.responseText))
}
})
function appendHtml(news){
var html = ''
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>'
}
$('.news').innerHTML = html
}
function $(selector){
return document.querySelector(selector)
}
</script>
</html>