c++ 外部元件發生異常_Vue元件化開發之插槽
技術標籤:計算機網路(HTTP)Node.js
跨域
1. JSONP:
只要說到跨域,就必須聊到 JSONP,JSONP全稱為:JSON with Padding,可用於解決主流瀏覽器的跨域資料訪問的問題。
Web 頁面上呼叫 js 檔案不受瀏覽器同源策略的影響,所以通過 Script 便籤可以進行跨域的請求:
- 首先前端先設定好回撥函式,並將其作為 url 的引數。
- 服務端接收到請求後,通過該引數獲得回撥函式名,並將資料放在引數中將其返回
- 收到結果後因為是 script 標籤,所以瀏覽器會當做是指令碼進行執行,從而達到跨域獲取資料的目的。
例項:
後端邏輯:
//server.js
const url = require('url');
require('http').createServer((req, res) => {
const data = {
x: 10
};
const callback = url.parse(req.url, true).query.callback;
res.writeHead(200);
res.end(`${callback}(${JSON.stringify(data)})`);
}).listen(3000, '127.0.0.1');
console.log('啟動服務,監聽 127.0.0.1:3000' );
通過 node server.js 啟動服務,監聽埠 3000,這樣服務端就建立起來了
前端頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<script>
function jsonpCallback(data) {
alert('獲得 X 資料:' + data.x);
}
</script>
<script src="http://127.0.0.1:3000?callback=jsonpCallback"></script>
</body>
</html>
邏輯已經寫好了,那如何來模擬一個跨域的場景呢?
這裡我們通過埠號的不同來模擬跨域的場景,通過 http://127.0.0.1:8080 埠來訪問頁面。先通過 npm 下載 http-server 模組:
nom install -g http-server
並且在頁面同目錄下輸入:
http-server
這樣就可以通過埠 8080 訪問 index.html 剛才那個頁面了,相當於是開啟兩個監聽不同埠的 http 伺服器,通過頁面中的請求來模擬跨域的場景。開啟瀏覽器,訪問 http://127.0.0.1:8080 就可以看到從 http://127.0.0.1:3000 獲取到的資料了。
至此,通過 JSONP 跨域獲取資料已經成功了,但是通過這種事方式也存在著一定的優缺點:
優點:
- 它不像XMLHttpRequest 物件實現 Ajax 請求那樣受到同源策略的限制
- 相容性很好,在古老的瀏覽器也能很好的執行
- 不需要 XMLHttpRequest 或 ActiveX 的支援;並且在請求完畢後可以通過呼叫 callback 的方式回傳結果。
缺點:
- 它支援 GET 請求而不支援 POST 等其它類行的 HTTP 請求。
- 它只支援跨域 HTTP 請求這種情況,不能解決不同域的兩個頁面或 iframe 之間進行資料通訊的問題
2. CORS:
CORS 是一個 W3C 標準,全稱是"跨域資源共享"(Cross-origin resource sharing)它允許瀏覽器向跨源伺服器,發出 XMLHttpRequest 請求,從而克服了 ajax 只能同源使用的限制。
CORS 需要瀏覽器和伺服器同時支援才可以生效,對於開發者來說,CORS 通訊與同源的 ajax 通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現 ajax 請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。
因此,實現 CORS 通訊的關鍵是伺服器。只要伺服器實現了 CORS 介面,就可以跨源通訊。
首先前端先建立一個 index.html 頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS</title>
</head>
<body>
<script>
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:3000', true);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText);
}
}
xhr.send(null);
</script>
</body>
</html>
這似乎跟一次正常的非同步 ajax 請求沒有什麼區別,關鍵是在服務端收到請求後的處理:
require('http').createServer((req, res) => {
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost:8080'
});
res.end('這是你要的資料:1111');
}).listen(3000, '127.0.0.1');
console.log('啟動服務,監聽 127.0.0.1:3000');
關鍵是在於設定相應頭中的 Access-Control-Allow-Origin,該值要與請求頭中 Origin 一致才能生效,否則將跨域失敗。
接下來再次開啟兩個 http 伺服器程序:
開啟瀏覽器訪問 http://localhost:8080 就可以看到:
成功的關鍵在於 Access-Control-Allow-Origin 是否包含請求頁面的域名,如果不包含的話,瀏覽器將認為這是一次失敗的非同步請求,將會呼叫 xhr.onerror 中的函式。
CORS 的優缺點:
- 使用簡單方便,更為安全
- 支援 POST 請求方式
- CORS 是一種新型的跨域問題的解決方案,存在相容問題,僅支援 IE 10 以上
這裡只是對 CORS 做一個簡單的介紹,如果想更詳細地瞭解其原理的話,可以看看下面這篇文章:
3. Server Proxy:
伺服器代理,顧名思義,當你需要有跨域的請求操作時傳送請求給後端,讓後端幫你代為請求,然後最後將獲取的結果傳送給你。
假設有這樣的一個場景,你的頁面需要獲取 CNode:Node.js專業中文社群 論壇上一些資料,如通過 https://cnodejs.org/api/v1/topics,當時因為不同域,所以你可以將請求後端,讓其對該請求代為轉發。
程式碼如下:
const url = require('url');
const http = require('http');
const https = require('https');
const server = 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) => {
let data = "";
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8'
});
res.end(data);
});
})
}
}).listen(3000, '127.0.0.1');
console.log('啟動服務,監聽 127.0.0.1:3000');
通過程式碼你可以看出,當你訪問 http://127.0.0.1:3000 的時候,伺服器收到請求,會代你傳送請求 https://cnodejs.org/api/v1/topics 最後將獲取到的資料傳送給瀏覽器。
同樣地開啟服務:
開啟瀏覽器訪問 http://localhost:3000/topics,就可以看到
跨域請求成功。
純粹的跨全域請求的方式已經介紹完了,另外介紹四種通過 iframe 跨域與其它頁面通訊的方式。
4. location.hash:
在 url 中,http://www.baidu.com#helloworld 的 “#helloworld” 就是 location.hash,改變 hash 值不會導致頁面重新整理,所以可以利用 hash 值來進行資料的傳遞,當然資料量是有限的。
假設 localhost:8080 下有檔案 cs1.html 要和 localhost:8081 下的 cs2.html 傳遞訊息,cs1.html 首先建立一個隱藏的 iframe,iframe 的 src 指向 localhost:8081/cs2.html,這時的 hash 值就可以做引數傳遞。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CS1</title>
</head>
<body>
<script>
// http://localhost:8080/cs1.html
let ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = "http://localhost:8081/cs2.html#data";
document.body.appendChild(ifr);
function checkHash() {
try {
let data = location.hash ? location.hash.substring(1) : '';
console.log('獲得到的資料是:', data);
}catch(e) {
}
}
window.addEventListener('hashchange', function(e) {
console.log('獲得的資料是:', location.hash.substring(1));
});
</script>
</body>
</html>
cs2.html 收到訊息後通過 parent.location.hash 值來修改 cs1.html 的 hash 值,從而達到資料傳遞。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CS2</title>
</head>
<body>
<script>
// http://locahost:8081/cs2.html
switch(location.hash) {
case "#data":
callback();
break;
}
function callback() {
const data = "some number: 1111"
try {
parent.location.hash = data;
}catch(e) {
// ie, chrome 下的安全機制無法修改 parent.location.hash
// 所以要利用一箇中間的代理 iframe
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://localhost:8080/cs3.html#' + data; // 該檔案在請求域名的域下
document.body.appendChild(ifrproxy);
}
}
</script>
</body>
</html>
由於兩個頁面不在同一個域下IE、Chrome不允許修改parent.location.hash的值,所以要藉助於 localhost:8080 域名下的一個代理 iframe 的 cs3.html 頁面
<script>
parent.parent.location.hash = self.location.hash.substring(1);
</script>
之後老規矩,開啟兩個 http 伺服器:
這裡為了圖方便,將 cs1,2,3 都放在同個資料夾下,實際情況的話 cs1.html 和 cs3.html 要與 cs2.html 分別放在不同的伺服器才對。
之後開啟瀏覽器訪問 localhost:8080/cs1.html,注意不是 8081,就可以看到獲取到的資料了,此時頁面的 hash 值也已經改變。
當然這種方法存在著諸多的缺點:
- 資料直接暴露在了 url 中
- 資料容量和型別都有限等等