跨域小知識

跨域:兩個地址間進行通信,當協議、域名、埠號任一不同稱為跨域,本文主要涉及的跨域方式有JSONP、CORS、降域和PostMessage。

後面所寫都根據下面目錄進行測試,site1代表localhost:7000相關的內容,site2代表localhost:8000相關的內容,我用express實現了兩個簡單的服務,就像這樣:

let express = require(express), app = express();app.use(express.static(public));app.listen(7000, function () { console.log(running...);});

目錄結構


JSONP:我想點擊localhost:7000下的按鈕,獲取localhost:8000/jsonp介面的數據

上圖數據(localhost:8000/jsonp)正常返回時,服務端代碼一般如下寫法:

app.get(/jsonp, function (req, res) { res.send({ "content": "知乎,發下更大的世界~" });});

前端跨域請求數據時,若想利用JSONP的方法,服務端(localhost:8000/jsonp)代碼需改造如下:

app.get(/jsonp, function (req, res) { let fnName = req.query.callback, data = JSON.stringify({ "content": "知乎,發下更大的世界~" }); let callback = fnName + ( + data + );; res.send(callback);});

此時localhost:7000下的前端代碼改造如下:

<script>function showCon(data) { console.log(data.content);}</script><script src="http://127.0.0.1:8000/jsonp?callback=showCon"></script>

刷新localhost:7000地址,可以發現還沒等我點擊請求數據按鈕,內容立馬被列印出來了,這樣不可控的數據返回並不是我們需要的。

仔細觀察前端代碼,會發現showCon函數是在script標籤中被調用的,所以我們可以在點擊按鈕時再創建script標籤,來控制代碼的執行,改造如下:

<script>// 注意這個showCon必須是個全局的函數,不然script中執行的時候找不到function showCon(data) { console.log(data.content);}let getData = (function () { // 創建script標籤 function createScript(url) { let oS = document.createElement("script"); oS.setAttribute(type, text/javascript); oS.src = url; document.body.appendChild(oS); } // 初始化 function init() { let oBtn = document.querySelector("button"), url = "http://127.0.0.1:8000/jsonp?callback=showCon"; oBtn.addEventListener("click", () => { createScript(url); }) } return { init: init }})();getData.init();</script>

總結:JSONP方式前端需要利用script標籤無同源限制的特性;後端正常返回的數據需要包裹在前端需要執行的回調函數內返回,這句話怎麼理解,代碼如下:

// 這叫後端正常返回res.send({ "content": "知乎,發下更大的世界~"});// 這叫需要包裹在前端需要執行的回調函數內返回res.send(showCon({ "content": "知乎,發下更大的世界~" }));


CORS(Cross-origin resource sharing):這種方式不需要前端參與,瀏覽器會自動幫我們在請求頭中添加需要的內容,主要在於後端的配置。

前端利用AJAX正常請求數據,代碼如下:

<script>let getData = (function () { function ajax() { var xhr = new XMLHttpRequest(); // 前端該怎麼寫還是怎麼寫,直接進行數據請求 xhr.open(GET, http://127.0.0.1:8000/jsonp, true); xhr.send() xhr.onreadystatechange = function () { if (xhr.readyState === 4 & xhr.status === 200) { console.log(xhr.responseText); } } } function init() { let oBtn = document.querySelector("button"); oBtn.addEventListener("click", () => { ajax(); }) } return { init: init };})();getData.init();</script>

後端代碼:

app.get(/jsonp, function (req, res) { // 關鍵的一句 res.header("Access-Control-Allow-Origin", "*"); res.send({ content: "知乎,發下更大的世界~" });});


降域:這種常用於iframe之間的跨域,我想在a.zhihu.com下改變b.zhihu.com的內容,例如下圖:

a.zhihu.com下的前端代碼改造如下:

<script>document.domain = "zhihu.com";window.onload = function () { let oFrame = window.frames[0].document.querySelector("#div1"); oFrame.innerHTML = "hello world";};</script>

b.zhihu.com下的前端代碼改造如下:

<script>document.domain = "zhihu.com";</script>


PostMessage形式:a.zhihu.com下輸入內容,其鏈接的iframe(b.zhihu.com)輸入框跟著變化,同理iframe中輸入框變化,a.zhihu.com中輸入框內容也跟著改變。

a.zhihu.com前端代碼改造如下:

<script src="https://cdn.bootcss.com/jquery/1.11.2/jquery.js"></script><script>$("#input1").on(input, function (e) { window.frames[0].postMessage($(this).val(), "*");})window.addEventListener(message, function (e) { $("#input1").val(e.data);});</script>

b.zhihu.com前端代碼改造如下:

<script src="https://cdn.bootcss.com/jquery/1.11.2/jquery.js"></script><script>$("#input1").on(input, function (e) { window.parent.postMessage($(this).val(), "*")})window.addEventListener(message, function (e) { $("#input1").val(e.data);});</script>

查看源代碼

參考:跨域資源共享 CORS 詳解

題圖來源:Lucas Clara


推薦閱讀:

【乾貨分享】前端面試知識點錦集(CSS篇)——附答案
前端日刊-2018.01.20
Weex Ui 半年開源之路
為什麼要選擇一個前端框架?
JS刷題總結

TAG:跨域 | 前端開發 |