跨域的幾種解決方案:
一、JSONP(jsonp)
概念:
JSONP(JSON with Padding,填充式 JSON 或參數(shù)式 JSON)是一種通過
優(yōu)點:
- 簡單易用
- 兼容性好,支持各種瀏覽器
缺點:
- 只能實現(xiàn) GET 請求,無法實現(xiàn) POST 等其他類型的請求
- 安全性較低,容易受到 XSS 攻擊
Eg:
為什么需要動態(tài)生成標簽,而不是直接通過
原因有以下幾點:
- 避免腳本注入攻擊:如果直接將需要請求的數(shù)據(jù)以及回調(diào)函數(shù)名稱寫在
二、 CORS(Cross-Origin Resource Sharing)(cors)
概念:
CORS 是一種通過服務(wù)器設(shè)置響應(yīng)頭信息,允許指定域名的請求跨域訪問資源,從而實現(xiàn)跨域資源共享的技術(shù)。具體而言,在服務(wù)端設(shè)置 Access-Control-Allow-Origin 響應(yīng)頭字段來指定允許的來源域名,以及其他相關(guān)的響應(yīng)頭信息。
優(yōu)點:
- 支持各種請求類型,包括 GET、POST、PUT 等
- 安全性比 JSONP 較高,不容易受到 XSS 攻擊
缺點:
- 需要服務(wù)端進行配合,設(shè)置相應(yīng)的響應(yīng)頭信息
- 不是所有瀏覽器都支持 CORS(例如 IE8 和 IE9)
如何配置cors:
在服務(wù)端實現(xiàn) CORS 跨域請求,需要配置相應(yīng)的 HTTP 響應(yīng)頭。常見的響應(yīng)頭字段包括:
- Access-Control-Allow-Origin:指定允許跨域請求的來源域名,如果希望允許所有來源域名,則可以設(shè)置為 *。
- Access-Control-Allow-Methods:指定允許跨域請求的 HTTP 方法,如 GET、POST、PUT 等。多個方法之間使用逗號分隔。
- Access-Control-Allow-Headers:指定允許跨域請求的自定義請求頭,多個請求頭之間使用逗號分隔。
- Access-Control-Allow-Credentials:用于指示是否允許發(fā)送 Cookie 以及其他憑證信息到跨域請求中。如果設(shè)置為 true,則說明請求可以發(fā)送身份憑證。如果未設(shè)置或設(shè)置為 false,則不能發(fā)送憑證。
下面是一個 Node.js Express 框架的示例代碼:
const express = require('express');
const app = express();
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});
// 處理路由和請求
// ...
app.listen(8080);
上述代碼中,我們通過設(shè)置響應(yīng)頭來實現(xiàn) CORS 跨域請求的支持。需要注意的是,如果設(shè)置了 Access-Control-Allow-Credentials 為 true,則在服務(wù)端也需要設(shè)置 req.header(‘Access-Control-Allow-Credentials’, ‘true’) 來響應(yīng)憑證信息。此外,CORS 跨域請求還需要確??蛻舳撕头?wù)端的協(xié)議、域名和端口號均相同或允許跨域。
三、代理服務(wù)器(proxy)
概念:
代理服務(wù)器是一種通過在同一個域名下設(shè)置一個代理服務(wù)器,將需要跨域訪問的請求先發(fā)送到代理服務(wù)器上,再由代理服務(wù)器向目標服務(wù)器發(fā)出請求,并將請求結(jié)果返回給客戶端的技術(shù)。這樣客戶端就可以避免直接訪問跨域服務(wù)器,從而可以繞開瀏覽器的同源策略。
優(yōu)點:
- 可以實現(xiàn)各種請求類型,包括 GET、POST、PUT 等
- 安全性較高,不容易受到 XSS 攻擊
缺點:
- 需要額外的代理服務(wù)器來進行轉(zhuǎn)發(fā),增加了服務(wù)器的負擔(dān)
- 需要進行額外的配置和編碼
舉個常規(guī)的配置例子:
在 dva 項目中配置 proxy 可以通過修改 package.json 文件來實現(xiàn)。下面是一個詳細的 dva 項目配置 proxy 的示例:
1. 在 dva 項目根目錄下創(chuàng)建一個名為 setupProxy.js 的文件,用于配置代理。示例代碼如下:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api', // 需要代理的請求前綴
proxy({
target: 'http://localhost:8080', // 設(shè)置代理目標地址
changeOrigin: true, // 如果代理服務(wù)器和目標服務(wù)器協(xié)議、域名或端口不同,需要設(shè)置為 true
pathRewrite: {
'^/api': '' // 將請求路徑的前綴 /api 去掉
}
})
);
};
上述代碼配置了一個代理,將所有以 /api 開頭的請求轉(zhuǎn)發(fā)到 http://localhost:8080/ 上,并將請求路徑的前綴 /api 去掉。如果代理服務(wù)器和目標服務(wù)器協(xié)議、域名或端口不同,需要將 changeOrigin 設(shè)置為 true。
2. 在 package.json 文件中添加 “proxy” 字段,指定代理配置文件的路徑。示例代碼如下:
{
"name": "my-dva-app",
"version": "0.1.0",
"dependencies": {
// ...
},
"scripts": {
// ...
},
"proxy": "http://localhost:3000/",
}
上述代碼中,我們將 “proxy” 字段設(shè)置為 http://localhost:3000/,這意味著所有以 /api 開頭的請求會被轉(zhuǎn)發(fā)到 http://localhost:3000/api 上。
3. 在 dva model 中使用 fetch 或者 dva-loading 等異步請求方式時,只需要在 URL 中添加前綴 /api 即可。示例代碼如下:
import { delay } from 'dva/saga';
import request from '../utils/request';
export default {
namespace: 'example',
state: {
data: null,
},
effects: {
*fetchData(_, { call, put }) {
yield call(delay, 1000); // 模擬延遲
const result = yield call(request, '/api/data'); // 請求地址為 /api/data
yield put({
type: 'saveData',
payload: result.data,
});
},
},
reducers: {
saveData(state, { payload }) {
return {
...state,
data: payload,
};
},
},
};
上述代碼中,我們在 effects 的 fetchData 方法中使用了 request 工具函數(shù)來發(fā)起一個 GET 請求,請求的地址為 /api/data,實際上會被代理轉(zhuǎn)發(fā)到 http://localhost:8080/data 上。無論是 fetch 還是使用 dva-loading,只要在 URL 中加上 /api 前綴即可。
四、postMessage(postmessage)
概念:
postMessage 是一種在不同窗口之間通過 postMessage 方法發(fā)送消息,從而實現(xiàn)數(shù)據(jù)的跨域傳輸?shù)募夹g(shù)。具體而言,在前端頁面中使用 postMessage 方法來向目標域名發(fā)送一條消息,并在目標頁面中通過監(jiān)聽 message 事件來接收并處理該消息。
優(yōu)點:
- 可以實現(xiàn)各種請求類型,包括 GET、POST、PUT 等
- 安全性較高,不容易受到 XSS 攻擊
缺點:
- 需要瀏覽器支持 HTML5,不兼容 IE8 及以下版本的瀏覽器
- 不支持傳輸大量數(shù)據(jù)
如何使用postMessage實現(xiàn)父窗口<–>子窗口通信:
在實際應(yīng)用場景中,我們可能需要在不同的窗口/iframe 中進行通信,比如說,在一個頁面中內(nèi)嵌了多個 iframe 窗口,這些窗口之間需要進行數(shù)據(jù)交互。如果這些窗口處于同一個域名下,我們就可以通過 JavaScript 變量或者全局事件來進行通信,但是如果它們處于不同的域名下,我們就無法直接進行通信了。
這時候,就可以使用 postMessage
來完成跨域通信了。具體實現(xiàn)步驟如下:
子—>父
父窗口
- 在父窗口中創(chuàng)建一個 iframe 窗口,并指定它的 src 屬性為要加載的子窗口頁面。
- 在父窗口中監(jiān)聽子窗口發(fā)送的消息,并在處理函數(shù)中獲取到消息內(nèi)容,進行相應(yīng)的處理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parent Window</title>
</head>
<body>
<!-- 創(chuàng)建一個 iframe 窗口 -->
<iframe id="childWindow" src="http://example.com"></iframe>
<script>
// 監(jiān)聽子窗口發(fā)送的消息
window.addEventListener('message', (event) => {
// 首先判斷消息來源是否合法
if (event.origin === 'http://example.com') {
// 獲取消息內(nèi)容并進行處理
const message = event.data;
console.log('Received message from child window:', message);
}
});
</script>
</body>
</html>
在上面的代碼中,我們在父窗口中創(chuàng)建了一個 iframe
窗口,并加載了一個跨域網(wǎng)站(http://example.com)。在父窗口中,我們使用 window.addEventListener()
方法監(jiān)聽 message
事件,當子窗口向父窗口發(fā)送消息時會觸發(fā)該事件。在事件處理函數(shù)中,我們首先判斷消息來源是否合法,如果合法,則獲取到消息的內(nèi)容并進行相應(yīng)的處理。
子窗口
- 在子窗口中使用
window.parent
獲取到父窗口對象,并向父窗口發(fā)送消息。 - 在子窗口中監(jiān)聽父窗口發(fā)送的消息,并在處理函數(shù)中獲取到消息內(nèi)容,進行相應(yīng)的處理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Child Window</title>
</head>
<body>
<script>
// 向父窗口發(fā)送消息
const targetOrigin = 'http://localhost:8080';
const parentWindow = window.parent;
parentWindow.postMessage('Hello, Parent Window!', targetOrigin);
// 監(jiān)聽父窗口發(fā)送的消息
window.addEventListener('message', (event) => {
// 首先判斷消息來源是否合法
if (event.origin === 'http://localhost:8080') {
// 獲取消息內(nèi)容并進行處理
const message = event.data;
console.log('Received message from parent window:', message);
}
});
</script>
</body>
</html>
在上面的代碼中,我們在子窗口中使用 window.parent
獲取到父窗口對象,并使用 postMessage
方法向父窗口發(fā)送了一條消息。在最后一行代碼中,targetOrigin
參數(shù)指定了目標窗口的域名和端口號,以確保消息能夠被正確地發(fā)送到父窗口。在子窗口中,我們同樣監(jiān)聽 message
事件,并在事件處理函數(shù)中獲取到父窗口發(fā)送的消息內(nèi)容。
父–>子:
要通過 postMessage
實現(xiàn)父窗口向子窗口發(fā)送數(shù)據(jù),可以在父窗口中使用 iframe
元素的 contentWindow
屬性獲取到子窗口的 window 對象,然后調(diào)用該對象的 postMessage()
方法來發(fā)送消息。
具體實現(xiàn)步驟如下:
父窗口
- 在父窗口中創(chuàng)建一個 iframe 窗口,并指定它的 src 屬性為要加載的子窗口頁面。
- 在需要發(fā)送數(shù)據(jù)的時候,使用
contentWindow
屬性獲取到子窗口的 window 對象,并調(diào)用它的postMessage()
方法發(fā)送消息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parent Window</title>
</head>
<body>
<!-- 創(chuàng)建一個 iframe 窗口 -->
<iframe id="childWindow" src="http://example.com"></iframe>
<script>
const childWindow = document.getElementById('childWindow');
// 發(fā)送數(shù)據(jù)給子窗口
function sendDataToChild(data) {
const targetOrigin = 'http://example.com';
childWindow.contentWindow.postMessage(data, targetOrigin);
}
// 在需要發(fā)送數(shù)據(jù)的時候調(diào)用 sendDataToChild() 函數(shù)
sendDataToChild('Hello, Child Window!');
</script>
</body>
</html>
在上面的代碼中,我們在父窗口中創(chuàng)建了一個 iframe
窗口,并加載了一個跨域網(wǎng)站(http://example.com)。在父窗口中,我們通過 document.getElementById()
方法獲取到子窗口的 iframe
元素,然后使用 contentWindow
屬性獲取到子窗口的 window 對象。在 sendDataToChild()
函數(shù)中,我們調(diào)用該對象的 postMessage()
方法來向子窗口發(fā)送數(shù)據(jù)。
需要注意的是,在調(diào)用 postMessage()
方法時,第一個參數(shù)為要發(fā)送的數(shù)據(jù),可以是任意類型的數(shù)據(jù),比如字符串、數(shù)字、數(shù)組等;第二個參數(shù)為目標窗口的源(origin),可以指定一個字符串或者一個 URL 對象,用來控制數(shù)據(jù)發(fā)送的目標。如果不需要限制目標窗口,可以傳入 ‘*’。具體使用時需要根據(jù)實際需求來設(shè)置。
綜上所述,通過使用 postMessage
,我們可以在不同的窗口/iframe 或者跨域時進行跨文檔通信,實現(xiàn)數(shù)據(jù)的傳遞和交互。需要注意的是,在實際應(yīng)用中,我們需要根據(jù)具體的需求和安全性考慮來選擇合適的參數(shù)和方式進行配置和使用。
五、WebSocket(websocket)
概念:
WebSocket 是一種使用 HTTP 協(xié)議進行握手,建立一條持久性的連接,并通過該連接實現(xiàn)數(shù)據(jù)的實時推送和跨域傳輸?shù)募夹g(shù)。
它允許客戶端和服務(wù)器之間建立持久連接,使得雙方可以實時地傳遞數(shù)據(jù)。相比于傳統(tǒng)的 HTTP 請求-響應(yīng)模式,WebSockets 可以減少網(wǎng)絡(luò)延遲,提高數(shù)據(jù)傳輸?shù)男?,同時也支持服務(wù)端向客戶端主動推送數(shù)據(jù)。
WebSockets 協(xié)議實現(xiàn)了一個基于幀的消息傳遞機制,通過使用 WebSocket API,開發(fā)者可以方便地實現(xiàn)復(fù)雜的實時數(shù)據(jù)傳輸場景,比如在線聊天、多人游戲等等。
優(yōu)點:
- 建立的連接是持久性的,可以輕松地實現(xiàn)實時推送和通信
- 支持雙向數(shù)據(jù)傳輸
- 安全性較高,不容易受到 XSS 攻擊
缺點:
- 需要瀏覽器和服務(wù)端同時支持 WebSocket 技術(shù)
- 建立連接時需要進行額外的握手,增加了網(wǎng)絡(luò)開銷
如何使用 WebSockets:
服務(wù)器端
在服務(wù)器端,我們需要創(chuàng)建一個 WebSocket 的實例,并監(jiān)聽 connection
事件,在該事件處理函數(shù)中,可以獲取到客戶端連接的 WebSocket
對象,并通過它來處理后續(xù)的通信。
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('A new client connected.');
// 監(jiān)聽客戶端發(fā)送的消息
ws.on('message', (data) => {
console.log('Received message from client:', data);
// 發(fā)送消息給客戶端
ws.send('Hello, Client!');
});
});
在上面的代碼中,我們首先引入了 Node.js 的 WebSocket 模塊,并創(chuàng)建了一個 WebSocket 的服務(wù)器實例,監(jiān)聽本地的 8080 端口。在 connection
事件處理函數(shù)中,我們可以獲取到客戶端連接的 WebSocket
對象,并通過它來監(jiān)聽 message
事件和發(fā)送消息。
客戶端
在客戶端,我們同樣需要創(chuàng)建一個 WebSocket 的實例,并連接到服務(wù)器。在連接成功后,可以通過該實例來監(jiān)聽服務(wù)器發(fā)送的消息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Example</title>
</head>
<body>
<script>
const socket = new WebSocket('ws://localhost:8080');
// 監(jiān)聽連接成功事件
socket.onopen = (event) => {
console.log('Connected to server.');
// 發(fā)送消息給服務(wù)器
socket.send('Hello, Server!');
};
// 監(jiān)聽服務(wù)器發(fā)送的消息
socket.onmessage = (event) => {
console.log('Received message from server:', event.data);
};
</script>
</body>
</html>
在上面的代碼中,我們創(chuàng)建了一個 WebSocket 的實例,并連接到服務(wù)器的地址(ws://localhost:8080)。在連接成功后,onopen
回調(diào)函數(shù)會被觸發(fā),此時我們可以通過 send()
方法向服務(wù)器發(fā)送消息??蛻舳诉€可以監(jiān)聽 onmessage
回調(diào)函數(shù),從而獲取服務(wù)器發(fā)送的消息內(nèi)容。文章來源:http://www.zghlxwxcb.cn/news/detail-722293.html
綜上所述,WebSocket 是一種實現(xiàn)雙向通信的協(xié)議,其使用方式與傳統(tǒng)的 HTTP 協(xié)議有一些不同,需要開發(fā)者進行相應(yīng)的調(diào)整和適配。在實際應(yīng)用中,我們需要根據(jù)具體的需求和場景來選擇合適的協(xié)議和工具來實現(xiàn)數(shù)據(jù)通信。文章來源地址http://www.zghlxwxcb.cn/news/detail-722293.html
到了這里,關(guān)于前端常見跨域解決方案(jsonp,cors,proxy,postMessage,webSocket)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!