跨域概述
為什么會(huì)有跨域
跨域解決辦法:1沟蔑、jsonp湿诊;2、后臺(tái)代理
手寫實(shí)現(xiàn)jsonp跨域(包括服務(wù)器端代碼)
跨域問題
A服務(wù)器上的頁(yè)面去獲取B服務(wù)器上的資源瘦材,由于XMLHttpRequest同源策略限制厅须,A頁(yè)面是獲取不到數(shù)據(jù)的。
同源:協(xié)議食棕、域名朗和、端口號(hào)任何一個(gè)都要相同,否則就會(huì)出現(xiàn)跨域請(qǐng)求簿晓。
為什么會(huì)跨域
為了安全
解決辦法
方法一:jsonp
原理:使用script標(biāo)簽可以跨域請(qǐng)求資源的特性眶拉,去發(fā)送請(qǐng)求,得到數(shù)據(jù)
- 核心思想
動(dòng)態(tài)在頁(yè)面創(chuàng)建script標(biāo)簽抢蚀,然后給src設(shè)置url镀层,這個(gè)url就是跨域請(qǐng)求的鏈接,然后在頁(yè)面聲明一個(gè)方法皿曲,這個(gè)方法名要和后臺(tái)返回的數(shù)據(jù)里帶有的名字一樣唱逢,一般情況下,這個(gè)名字是動(dòng)態(tài)設(shè)置的屋休,在url中傳給后臺(tái)坞古。 - 代碼示例
// localhost:9528 服務(wù)器
// index.html
<body>
<script>
function getJsonp(data){
console.log(data)
}
</script>
<script src='http://lcoalhost:9527/getData?call=getJson'></script>
</body>
// localhost:9527 服務(wù)器
// json/getData 接口 (基于express框架)
router.get("/getData", function (req, res) {
res.send("getJson({a:1,b:2,c:3})")
});
-
效果圖
輸出結(jié)果 總結(jié)
當(dāng)script標(biāo)簽去加載 getData?call=getJson 接口時(shí)(就當(dāng)是去請(qǐng)求一個(gè)js文件),接口返回?cái)?shù)據(jù)后劫樟,瀏覽器會(huì)去執(zhí)行得到的數(shù)據(jù)痪枫,這個(gè)時(shí)候?yàn)g覽器發(fā)現(xiàn)數(shù)據(jù)是 getJson({a:1,b:2,c:3}) 织堂,那這個(gè)getJSON是一個(gè)方法呀,所以他會(huì)立即執(zhí)行這個(gè)方法奶陈,而這個(gè)上面我們已經(jīng)定義好了易阳,所以控制臺(tái)就打印除了數(shù)據(jù),如上圖吃粒。
這只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn)原理說(shuō)明潦俺,現(xiàn)在有成熟的庫(kù)可以使用 json ,如果有時(shí)間,后期將出關(guān)于jsonp框架源碼分析的文章徐勃。
- jsonp的缺點(diǎn):只能解決文本類型的數(shù)據(jù)跨域事示,如果是圖片呢?那就得需要下面這種方式了僻肖。
方法二:后臺(tái)代理
原理:自己搭建一個(gè)服務(wù)器肖爵,提供一個(gè)接口,以供前端頁(yè)面使用臀脏,前端頁(yè)面把url鏈接發(fā)送給后臺(tái)劝堪;然后接到參數(shù)后,服務(wù)器發(fā)送http請(qǐng)求(這兒可以設(shè)置各種請(qǐng)求頭)谁榜,接到數(shù)據(jù)然后返回給頁(yè)面就行了幅聘。
-
使用場(chǎng)景:在之前寫的一個(gè)頁(yè)面,圖片路徑是用的微信公眾號(hào)發(fā)布的圖片窃植,但是在我的項(xiàng)目中請(qǐng)求這些圖片帝蒿,就會(huì)出現(xiàn)如下圖一樣的問題。
微信公眾號(hào)圖片防盜鏈
當(dāng)然巷怜,除了本文要講的方式之外葛超,還有其他解決辦法,比如 iframe 標(biāo)簽延塑,但是圖片過多的話绣张,這個(gè)方式就不太好了(我的頁(yè)面里面將全是iframe標(biāo)簽)。我的頁(yè)面是運(yùn)行在手機(jī)端的关带,所以還要考慮性能問題侥涵,只有把壓力交給了服務(wù)器。這是效果圖宋雏,里面圖片全是公眾號(hào)的圖片芜飘。
效果圖 - 代碼示例
// 前端代碼
<img src="http://host/micro-site/change?url=http://mmbiz.qpic.cn/mmbiz_jpg/ZHRvtsJlIIyCKYib6WAzImVbYEN9ljaUNIDtUMpAAAdkib2lVjPeUgI8GaNudwez1CUW9sHVLTrRoxzAoAPMicCgw/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1">
// 前面 host/micro-site/change 自己服務(wù)器的域名和接口
// url鏈接是圖片的路徑
// 服務(wù)器接到url后,發(fā)送http請(qǐng)求磨总,根據(jù)url請(qǐng)求數(shù)據(jù)嗦明,然后返回給前端頁(yè)面。
// 后端代碼 java
@GetMapping("change")
public void image(String url, HttpServletResponse response) {
response.setHeader("Content-Type", "image/*");
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Referer", "");
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36");
try {
CloseableHttpResponse execute = httpClient.execute(httpGet);
HttpEntity entity = execute.getEntity();
entity.writeTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
- 后臺(tái)代理缺點(diǎn):這種方式顯得有些笨重蚪燕,多了一層數(shù)據(jù)請(qǐng)求娶牌,本來(lái)發(fā)送到一個(gè)服務(wù)器就可以了奔浅,現(xiàn)在要通過兩臺(tái)服務(wù)器才能返回?cái)?shù)據(jù),速度相對(duì)慢了一些诗良。