跨域是指在浏览器端,由于浏览器的同源策略(Same Origin Policy)限制,导致无法直接访问来自其他域的资源。为了解决跨域问题,常用的方法包括使用 CORS(跨域资源共享)、JSONP、代理、WebSocket、跨文档消息传递(PostMessage)等。下面是对这些方法的简要说明以及代码示例:
1. CORS(跨域资源共享):
CORS 是一种标准的跨域解决方案,通过服务器设置响应头来允许跨域请求。
服务端示例(Node.js):
javascript
const express = require('express');
const app = express();
// 设置允许跨域访问的域名
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*'); // 或指定特定域名
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from server!' });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
客户端示例(JavaScript):
javascript
fetch('http://localhost:3000/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
2. JSONP:
JSONP 是一种利用 <script>
标签不受同源策略限制的特性来实现跨域请求的方法。
客户端示例(JavaScript):
javascript
function handleResponse(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'http://example.com/api/data?callback=handleResponse';
document.body.appendChild(script);
在上面的例子中,客户端向 example.com/api/data 发送请求,并指定了 callback 参数为 handleResponse 函数名。服务器返回的数据将会作为参数传入 handleResponse 函数中,并执行该函数。
3. 代理:
在项目中设置一个代理服务器,让代理服务器去请求目标服务器的数据,然后再将数据返回给前端。由于同源策略是浏览器的限制,代理服务器是在服务器端操作,因此可以绕过同源策略的限制。
例如,使用 Express 框架实现一个简单的代理服务器:
javascript
const express = require('express');
const axios = require('axios');
const app = express();
// 设置代理路由
app.get('/api/data', async (req, res) => {
try {
const response = await axios.get('http://example.com/api/data');
res.json(response.data);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Proxy server is running on http://localhost:${PORT}`);
});
4. WebSocket:
WebSocket 是一种基于 TCP 的全双工通信协议,它允许客户端和服务器之间进行双向通信,与传统的 HTTP 请求-响应模式不同,WebSocket 建立一次连接后,可以保持长时间的通信会话。
虽然 WebSocket 协议本身并不受同源策略的限制,但在使用 WebSocket 进行跨域通信时,仍然需要注意一些安全性问题。一种常见的做法是通过服务端设置 CORS 头部,允许特定的源访问 WebSocket 服务。
以下是一个使用 Node.js 和 ws
模块创建 WebSocket 服务器,并设置 CORS 头部的示例代码:
javascript
const WebSocket = require('ws');
const http = require('http');
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 设置 CORS 头部
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('WebSocket server is running');
});
// 启动 HTTP 服务器
server.listen(3000, () => {
console.log('HTTP server is running on http://localhost:3000');
});
// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ server });
// 监听 WebSocket 连接
wss.on('connection', (ws) => {
console.log('New WebSocket connection');
// 接收客户端消息
ws.on('message', (message) => {
console.log(`Received message: ${message}`);
// 发送消息给客户端
ws.send(`Server received message: ${message}`);
});
// 监听 WebSocket 关闭
ws.on('close', () => {
console.log('WebSocket connection closed');
});
});
在上面的示例中,我们首先创建了一个 HTTP 服务器,并设置了 CORS 头部,允许任何来源的请求访问该服务器。然后创建了一个 WebSocket 服务器,监听端口为 3000,当有 WebSocket 连接建立时,会输出一条日志信息。在收到客户端的消息后,服务器会将消息原样返回给客户端。
5. 跨文档消息传递(PostMessage):
跨文档消息传递(PostMessage)是一种在不同窗口或 iframe 之间安全地传递数据的机制,它允许在同源策略限制下的不同窗口之间进行通信。以下是一个简单的示例,演示了如何使用 PostMessage 在父窗口和子窗口之间进行通信:
父窗口 HTML:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Window</title>
</head>
<body>
<h1>Parent Window</h1>
<iframe id="childFrame" src="child.html" width="300" height="200"></iframe>
<script>
// 获取子窗口的引用
const childFrame = document.getElementById('childFrame').contentWindow;
// 发送消息给子窗口
childFrame.postMessage('Hello from parent window!', 'http://localhost:3000');
// 监听来自子窗口的消息
window.addEventListener('message', (event) => {
if (event.origin === 'http://localhost:3000') {
console.log('Received message from child window:', event.data);
}
});
</script>
</body>
</html>
子窗口 HTML(child.html):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Child Window</title>
</head>
<body>
<h1>Child Window</h1>
<script>
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
if (event.origin === 'http://localhost:3000') {
console.log('Received message from parent window:', event.data);
// 发送消息给父窗口
event.source.postMessage('Hello from child window!', event.origin);
}
});
</script>
</body>
</html>
在上面的示例中,父窗口和子窗口之间通过 PostMessage 进行了通信。父窗口首先获取了子窗口的引用,然后向子窗口发送了一条消息。子窗口监听来自父窗口的消息,收到消息后再向父窗口发送一条消息。两个窗口通过事件监听器监听对方的消息,并在收到消息后进行处理。
需要注意的是,PostMessage 方法的第二个参数 origin 是目标窗口的源,用于验证消息来源的安全性。在实际应用中,应该根据实际需求设置合适的 origin 值,确保消息的安全传递。
6.nginx反向代理
Nginx 来实现反向代理,通过反向代理可以解决跨域访问的问题。下面是一个简单的示例,演示了如何使用 Nginx 反向代理来转发请求:
假设有两个服务,一个运行在 http://localhost:3000
,另一个运行在 http://localhost:4000
,我们希望通过 Nginx 反向代理将请求转发到这两个服务上。
首先,需要安装 Nginx 并编辑配置文件。
安装 Nginx:
bash
# Ubuntu 或 Debian 系统
sudo apt update
sudo apt install nginx
# CentOS 或 RedHat 系统
sudo yum install nginx
编辑 Nginx 配置文件:
bash
sudo nano /etc/nginx/nginx.conf
在配置文件中添加以下内容:
bash
http {
server {
listen 80;
server_name localhost;
# 反向代理转发到第一个服务
location /service1/ {
proxy_pass http://localhost:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 反向代理转发到第二个服务
location /service2/ {
proxy_pass http://localhost:4000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
上面的配置中,我们在 Nginx 的配置文件中设置了两个 location
块,分别用于转发到第一个服务和第二个服务。通过 proxy_pass
指令可以指定目标服务的地址。proxy_set_header
指令用于设置代理请求头,确保目标服务能够正确获取到客户端的 IP 地址等信息。
保存并关闭文件后,重新加载 Nginx 配置:
bash
sudo nginx -s reload
现在,Nginx 将会在端口 80 上监听请求,并根据请求的路径将其转发到对应的服务上。例如,访问 http://localhost/service1/
将会转发到 http://localhost:3000/
,访问 http://localhost/service2/
将会转发到 http://localhost:4000/
。
通过这种方式,我们可以实现跨域访问,并将请求代理到不同的服务上。
7. 使用 window.name
+ iframe:
父窗口 HTML:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Window</title>
</head>
<body>
<h1>Parent Window</h1>
<iframe id="childFrame" src="child.html" style="display:none;"></iframe>
<script>
window.onload = function() {
var childFrame = document.getElementById('childFrame');
childFrame.onload = function() {
// 向子窗口传递消息
childFrame.contentWindow.name = 'Hello from parent window!';
};
};
</script>
</body>
</html>
子窗口 HTML(child.html):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Child Window</title>
</head>
<body>
<h1>Child Window</h1>
<script>
// 从父窗口读取消息
var message = window.name;
console.log('Message from parent window:', message);
</script>
</body>
</html>
8. 使用 location.hash
+ iframe:
父窗口 HTML:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Window</title>
</head>
<body>
<h1>Parent Window</h1>
<iframe id="childFrame" src="child.html" style="display:none;"></iframe>
<script>
window.onload = function() {
var childFrame = document.getElementById('childFrame');
childFrame.onload = function() {
// 向子窗口传递消息
childFrame.contentWindow.location.hash = 'Hello from parent window!';
};
};
</script>
</body>
</html>
子窗口 HTML(child.html):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Child Window</title>
</head>
<body>
<h1>Child Window</h1>
<script>
// 从父窗口读取消息
window.onload = function() {
var message = window.location.hash;
console.log('Message from parent window:', message);
};
</script>
</body>
</html>
9. 使用 document.domain
+ iframe:
父窗口 HTML:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Window</title>
</head>
<body>
<h1>Parent Window</h1>
<iframe id="childFrame" src="child.html" style="display:none;"></iframe>
<script>
window.onload = function() {
// 设置 document.domain
document.domain = 'example.com';
var childFrame = document.getElementById('childFrame');
childFrame.onload = function() {
// 向子窗口传递消息
childFrame.contentWindow.postMessage('Hello from parent window!', 'http://child.example.com');
};
};
</script>
</body>
</html>
子窗口 HTML(child.html):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Child Window</title>
</head>
<body>
<h1>Child Window</h1>
<script>
// 监听来自父窗口的消息
window.onload = function() {
window.addEventListener('message', function(event) {
console.log('Message from parent window:', event.data);
});
};
</script>
</body>
</html>
在上面的7,8,9示例中,父窗口通过 window.name
、location.hash
或 document.domain
向子窗口传递消息,子窗口则通过相应的方式接收消息,并在控制台输出。这些方法都可以用于实现简单的跨域通信。
10 Nodehttp-proxy-middleware
中间件代理
http-proxy-middleware
中间件可以用于在 Node.js 服务端设置代理,实现跨域请求的转发。即使涉及到两次跨域(客户端到代理服务器,代理服务器到目标服务器),http-proxy-middleware
也能够处理这种情况。
以下是一个简单的示例,演示了如何在 Node.js 中使用 http-proxy-middleware
中间件进行代理:
javascript
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 设置代理
const proxy = createProxyMiddleware({
target: 'http://example.com', // 目标服务器地址
changeOrigin: true, // 修改请求头中的 Host 字段为目标服务器地址
pathRewrite: {
'^/api': '/', // 重写请求路径,将 /api 开头的路径替换为空
},
});
// 使用代理中间件
app.use('/api', proxy);
// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Proxy server is running on http://localhost:${PORT}`);
});
在上述示例中,我们创建了一个代理中间件,将所有路径以 /api
开头的请求转发到目标服务器 http://example.com
上。通过 pathRewrite
选项,我们将请求路径中的 /api
前缀去除,以符合目标服务器的路径要求。
然后,我们将代理中间件应用到 Express 应用中,并启动了一个 Express 服务器,监听在本地的端口 3000 上。
客户端可以向这个 Express 服务器发送请求,请求路径以 /api
开头的请求将会被转发到目标服务器上,实现了代理转发功能。
最后也是全文最总要的,码字不易,欢迎点赞关注加搜藏,你的鼓励是我持之以恒的动力,感谢感谢感谢!!!