跨域問題的產(chǎn)生:
因?yàn)闉g覽器有同源策略,只有在同域名乾戏,同端口合搅,同協(xié)議的情況下才可以進(jìn)行數(shù)據(jù)交互;有的時(shí)候歧蕉,例如:在公司開發(fā)項(xiàng)目的時(shí)候灾部,前端開發(fā)的服務(wù)器可能和后端服務(wù)器不是同一個(gè),因?yàn)榭赡苁峭ㄟ^gulp惯退、webpack搭建的開發(fā)服務(wù)器赌髓,就需要解決跨域問題,再例如催跪,在大公司數(shù)據(jù)服務(wù)器不只有一個(gè)锁蠕,所以跨域問題也必然存在
一、jsonp解決跨域請(qǐng)求
jsonp是一種前后端結(jié)合的跨域方式懊蒸,原理就是通過script標(biāo)簽的src屬性來進(jìn)行數(shù)據(jù)請(qǐng)求荣倾,src不受同源策略的影響,故而能請(qǐng)求到數(shù)據(jù)骑丸,需要注意的是舌仍,數(shù)據(jù)處理需要通過回調(diào)函數(shù)來進(jìn)行,而本質(zhì)上通危,我們把回調(diào)函數(shù)的名字告訴后端铸豁,后端將數(shù)據(jù)放入到回調(diào)函數(shù)里,所以說需要告知后端菊碟,回調(diào)函數(shù)是什么节芥,這也就是為什么說jsonp是前后端結(jié)合的方式。
注意:一個(gè)script只能請(qǐng)求一次逆害,多次請(qǐng)求應(yīng)該去動(dòng)態(tài)的創(chuàng)建script头镊,回調(diào)函數(shù)也只能使用一次,所以也需要?jiǎng)討B(tài)創(chuàng)建 魄幕,使用完成后移除相艇,避免污染全局空間
缺點(diǎn):jsonp只能get請(qǐng)求
1、nodejs代碼梅垄,配置路由(使用express模塊)
//后端的任務(wù)厂捞,就是把數(shù)據(jù)放到前端的那個(gè)函數(shù)里并給它執(zhí)行一下
//直接返回這個(gè)操作的js字符串输玷,因?yàn)檫@個(gè)字符串會(huì)被script當(dāng)成js代碼來運(yùn)行
app.get('/data', function (req, res) {
?????????? //前端回調(diào)函數(shù)的名字
?????????? let fn = req.query['callback'];
????????? //注意,在content 后拼接一個(gè)空字符串靡馁,將獲取的數(shù)據(jù)轉(zhuǎn)義正常輸出欲鹏,否則有時(shí)候讀取的數(shù)據(jù)格式為buffer數(shù)據(jù)類型
?????????? var content = fs.readFileSync('./datas/data.json')+'';
?????????? //返回給前端函數(shù)數(shù)據(jù)
?????????? res.send(fn+'('+content+')');
});
2、js代碼臭墨,返回?cái)?shù)據(jù)(函數(shù)中用到的是es6解構(gòu)賦值參數(shù))
Jsonp({
????????? url:"http://localhost:3000/data",
???????? success(results){
??????? ? ?? console.log(results)
???????? }
})
function Jsonp({url,success}) {
??????? //動(dòng)態(tài)創(chuàng)建script標(biāo)簽
??????? let $script = $("")
??????? //隨機(jī)函數(shù)名字
??????? var random_name = 'random_fn_'+Date.now()
??????? //創(chuàng)建的隨機(jī)全局函數(shù)
??????? window[random_name] = function(data){ success(data)
?????? ? ? ? ?? //處理完數(shù)據(jù)之后赔嚎,將script刪掉,函數(shù)delete掉
??????????????? $script.remove()
??????????????? delete window[random_name]
??????? }
?????? $script[0].src = url+'?callback='+random_name
?????? $("body").append($script)
}
二胧弛、cors解決跨域請(qǐng)求
cors純后端的解決方式,每次請(qǐng)求都會(huì)有一個(gè)origin信息被后端捕捉尤误,后端可以通過設(shè)置對(duì)這個(gè)origin的域允許訪問來解決跨域問題
1、nodejs代碼
app.get("/data_cors",(req,res)=>{
//此次請(qǐng)求的源信息
var reqOrigin = req.header("origin");
//如果源信息存在结缚,且源是運(yùn)行訪問的
if(reqOrigin !=undefined && reqOrigin.indexOf("http://localhost:9000") > -1){
??????? //設(shè)置允許 http://localhost:3000 這個(gè)域響應(yīng)
??????? res.header("Access-Control-Allow-Origin", "http://localhost:9000");
?????? //允許這個(gè)域訪問
?????? res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
?????? //允許的請(qǐng)求方法
?????? res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
??????? }
??????? res.send('123')
})
2损晤、js代碼獲取返回的數(shù)據(jù)
$.ajax({
????????? url:"http://localhost:3000/data_cors",
????????? success(results){
????????????? console.log(results)
????????? }
})
三、服務(wù)端代理proxy解決跨域請(qǐng)求
因?yàn)榉?wù)器間的數(shù)據(jù)交互沒有跨域限制红竭,所以我們可以通過一個(gè)中間代理服務(wù)器來請(qǐng)求目標(biāo)服務(wù)器的數(shù)據(jù)尤勋,也就是開發(fā)服務(wù)器發(fā)送請(qǐng)求到代理服務(wù)器,代理服務(wù)器再請(qǐng)求目標(biāo)服務(wù)器茵宪,將數(shù)據(jù)返回給開發(fā)服務(wù)器
1最冰、nodejs代碼
//目標(biāo)服務(wù)器接口 3000:
app.get("/data_proxy",(req,res)=>{
????????? //此次請(qǐng)求的源信息
????????? res.send('proxy')
})
2、代理服務(wù)器1234:
app.get("/to3000",(req,res)=>{
???????? //代理服務(wù)器1234對(duì)9000做跨域處理
???????? var reqOrigin = req.header("origin");
???????? //如果源信息存在稀火,且源是運(yùn)行訪問的
???????? if(reqOrigin !=undefined && reqOrigin.indexOf("http://localhost:9000") > -1){
????? ? ? ? //設(shè)置允許 http://localhost:3000 這個(gè)域響應(yīng)
?????? ? ?? res.header("Access-Control-Allow-Origin", "http://localhost:9000");
??????? ? ? //允許這個(gè)域訪問
?????? res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
???? ? ? ?? //允許的請(qǐng)求方法
????? ? ? ? res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With"); }
?? ? ? ? ? //獲取到9000真正要調(diào)用的3000目標(biāo)服務(wù)器的接口
?? ? ? ? ? ? var url = req.query['url'] let _res = res
?? ? ? ? ??? //代理服務(wù)器1234像目標(biāo)服務(wù)器3000發(fā)送請(qǐng)求(沒有跨域限制)
??????????? http.get(url,(res)=>{ let result='' res.on("data",(chunk)=>{
??????????? ? ?? result+=chunk
?? ? ? ? ? })
???? ? ? ? res.on("end",()=>{ console.log(result)
???? ? ? ? ? ? ? //代理服務(wù)器1234將請(qǐng)求到的目標(biāo)服務(wù)器3000的數(shù)據(jù)返回給開發(fā)服務(wù)器9000
???? ? ????????? _res.send(result)
??? ? ? ? ? })
??? ?? })
})
app.listen(1234);
3暖哨、js代碼,開發(fā)服務(wù)器9000:
//當(dāng)前服務(wù)器9000向代理服務(wù)器1234發(fā)送請(qǐng)求
$.ajax({
??????? url:"http://localhost:1234/to3000",
?????? data:{
???????????? url:"http://localhost:3000/data_proxy"
?????? },
?????? success(results){
???????????? console.log(results)
?????? }
})