在使用Vue搭建的一個后端管理系統(tǒng)中蝌借,我使用axios請求本地的Node環(huán)境下的接口枯冈,但是請求失敗华坦,然后我錯誤信息是:
大概意思就是不能訪問
http://localhost:8080
我的Vue項目端口是
http://localhost:8081
撮躁,Node服務(wù)端運(yùn)行在http://localhost:8080
端口上实辑,也就是說因?yàn)檎埱蠖丝诤晚憫?yīng)端口不一致捺氢,所以請求失敗。我也在網(wǎng)上查看了一些關(guān)于跨域出現(xiàn)的原因及解決的方法徙菠,并記錄下來讯沈。
為什么會有跨域
跨域一句話的理解就是:服務(wù)端和請求端的地址不一樣。
什么是跨域
Ajax 的便利性大家都清楚婿奔,可以在不向服務(wù)器提交完整的頁面的情況下缺狠,實(shí)現(xiàn)局部更新頁面。但是瀏覽器處于對安全方面的考慮萍摊,不允許跨域調(diào)用其他頁面的對象挤茄。
其實(shí)這個也不能怪瀏覽器,假設(shè)誰都可以隨隨便便向你發(fā)送請求冰木,那樣有很大的安全隱患穷劈。
根據(jù)瀏覽器的同源策略, 只有當(dāng)協(xié)議,域名踊沸,端口相同的時候才算是同源, 反之則均視為是一個跨域的請求.
也就是說我剛剛的Vue端口是8081
歇终,服務(wù)端端口是8080
,端口不一樣逼龟,因?yàn)橥床呗缘拇嬖?评凝,所有我的請求會失敗。
一個問題腺律,當(dāng)找到了原因奕短,這個問題就解決了一半了。
怎么解決跨域
下面就先介紹三種跨全域的方法:
JSONP
應(yīng)該是最常見解決跨域的方法了匀钧,
他為什么能解決跨域呢翎碑,是因?yàn)閃eb 頁面上調(diào)用 js 文件不受瀏覽器同源策略的影響,所以通過 Script 便簽可以進(jìn)行跨域的請求:
- 首先前端先設(shè)置好回調(diào)函數(shù)之斯,并將其作為 url 的參數(shù)日杈。
- 服務(wù)端接收到請求后,通過該參數(shù)獲得回調(diào)函數(shù)名,并將數(shù)據(jù)放在參數(shù)中將其返回
- 收到結(jié)果后因?yàn)槭?script 標(biāo)簽达椰,所以瀏覽器會當(dāng)做是3腳本進(jìn)行運(yùn)行翰蠢,從而達(dá)到跨域獲取數(shù)據(jù)的目的。
我的前端是index.html
啰劲,后端是server.js
后端邏輯:
//server.js
const url = require('url');
const http = require('http');
http.createServer((req, res)=>{
const data = {
x: 10//返回的數(shù)據(jù)
};
const callback = url.parse(req.url, true).query.callback;
res.writeHead(200);
res.end(`${callback}(${JSON.stringify(data)})`);
//執(zhí)行回調(diào)函數(shù)梁沧,返回data
}).listen(3000, 'localhost');
console.log('啟動服務(wù),監(jiān)聽 localhost:3000');
然后使用node server.js
運(yùn)行
前端:
//index.html
<body>
<script>
function jsonpCallback(data) {
console.log('獲得 X 數(shù)據(jù):' + data.x);
}
</script>
<script src="http://localhost:3000?callback=jsonpCallback"></script>
</body>
之后打開index.html;就可以在控制臺看到返回的數(shù)據(jù)了:
至此蝇裤,通過 JSONP 跨域獲取數(shù)據(jù)已經(jīng)成功了廷支,jsonp這種方法跨域,他的兼容性很好,可以在古老的瀏覽器中國使用,因?yàn)檫@種方法是利用了<script>
標(biāo)簽的特殊性行楞,所有只支持GET請求。
CORS
CORS 是一個 W3C 標(biāo)準(zhǔn)施敢,全稱是"跨域資源共享"(Cross-origin resource sharing)它允許瀏覽器向跨源服務(wù)器,發(fā)出 XMLHttpRequest 請求狭莱,從而克服了 ajax 只能同源使用的限制僵娃。
CORS 需要瀏覽器和服務(wù)器同時支持才可以生效,對于開發(fā)者來說腋妙,CORS 通信與同源的 ajax 通信沒有差別默怨,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn) ajax 請求跨源骤素,就會自動添加一些附加的頭信息匙睹,有時還會多出一次附加的請求,但用戶不會有感覺济竹。
因此痕檬,實(shí)現(xiàn) CORS 通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了 CORS 接口送浊,就可以跨源通信谆棺。
前端:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
url:"http://127.0.0.1:3000",
success:function(res){
var res = JSON.parse(res);
$('body').text(res.data);
console.log(res.data);
}
});
</script>
這次前端啟動需要使用node-server
來啟動,使用npm install node-server
下載罕袋,然后當(dāng)前目錄下使用node-server
就可以了
后端:
const http = require('http');
http.createServer((req, res)=>{
const data = {
'data': 'Hello world'//返回的數(shù)據(jù)
};
res.writeHead(200, {'Access-Control-Allow-Origin': 'http://127.0.0.1:8080'});
//設(shè)置的頭部信息需要和前端請求的地址一致
res.end(JSON.stringify(data));
//返回data
}).listen(3000, '127.0.0.1');
console.log('啟動服務(wù),監(jiān)聽 127.0.0.1:3000');
使用命令node server.js
啟動碍岔;
CORS與JSONP的使用目的相同浴讯,但是比JSONP更強(qiáng)大。
JSONP只支持GET請求蔼啦,CORS支持所有類型的HTTP請求榆纽。JSONP的優(yōu)勢在于支持老式瀏覽器,以及可以向不支持CORS的網(wǎng)站請求數(shù)據(jù)。
Server Proxy
服務(wù)器代理奈籽,顧名思義饥侵,當(dāng)你需要有跨域的請求操作時發(fā)送請求給后端,讓后端幫你代為請求衣屏,然后最后將獲取的結(jié)果發(fā)送給你躏升。
假設(shè)有這樣的一個場景,你的頁面需要獲取 CNode:Node.js專業(yè)中文社區(qū) 論壇上一些數(shù)據(jù)狼忱,如通過 https://cnodejs.org/api/v1/topics膨疏,當(dāng)時因?yàn)椴煌颍阅憧梢詫⒄埱蠛蠖俗昱屍鋵υ撜埱蟠鸀檗D(zhuǎn)發(fā)佃却。
后端代碼如下:
const url = require('url');
const http = require('http');
const https = require('https');
http.createServer((req, res)=>{
const path = url.parse(req.url).path.slice(1);
//核對請求路由是否一致
if(path === 'topics'){
https.get('https://cnodejs.org/api/v1/topics', (resp)=>{
//https代發(fā)請求
let data='';
resp.on('data', chunk=>{
data+= chunk
});
resp.on('end', ()=>{
res.writeHead(
200,
{'Content-Type': 'application/json; charset=utf-8'}
);
res.end(data);
//返回數(shù)據(jù)
})
})
}
}).listen(3000, '127.0.0.1');
console.log('啟動服務(wù),監(jiān)聽 127.0.0.1:3000');
前端代碼:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
url:"https://cnodejs.org/api/v1/topics",
success:function(res){
$('body').text(JSON.stringify(res));
console.log(res);
}
});
</script>
這樣就成功了
總結(jié)
常用的跨域方式基本就是這三種:
- JSONP
優(yōu)點(diǎn)是可以兼容老瀏覽器窘俺,缺點(diǎn)是只能發(fā)送GET請求 - CORS
優(yōu)點(diǎn)簡單方便饲帅,支持post請求,缺點(diǎn)是需要后端的配合,不支持老版瀏覽器瘤泪。灶泵。 - Server Proxy
優(yōu)點(diǎn)是前端正常發(fā)送ajax請求,缺點(diǎn)是后端會二次請求均芽。
其他的跨域方式還有:location.hash
丘逸、window.name
、postMessage
等方式掀宋,有時間也可以了解一下深纲。
參考資料:
- 跨域資源共享 CORS 詳解[阮一峰的博客]:http://www.ruanyifeng.com/blog/2016/04/cors.html
- 關(guān)于跨域,你想知道的全在這里:https://zhuanlan.zhihu.com/p/25778815
- 不要再問我跨域的問題了[sf]:https://segmentfault.com/a/1190000015597029
- 關(guān)于跨域,以及跨域的幾種方式[cnblog]:https://www.cnblogs.com/chenshishuo/p/4919224.html
- 瀏覽器的同源策略[MDN]:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy