什么是跨域问题
跨域问题”是指浏览器为了安全考虑而实施的一种限制策略,称为“同源策略”(Same-origin policy)。同源策略规定一个网页中的脚本只能读取或操作来自同一源的资源。这里的“源”由协议,域名和端口号三部分组成。如果两个页面的这三部分有任何一项不同,则这两个页面被视为来自不同的 源。
假如我们有一串Http 地址:www.example.com:8080/path/to/res…
- 协议:HTTP
- 域名:www.example.com
- 端口:8080
- 路径:/path/to/resource
我们要保证协议,域名,端口3个相同才能进行访问
我们自己做的项目,前后端可能不在一个端口,这个时候,就会出现跨域问题,那么怎么解决跨域问题呢?
解决跨域问题常见的几种方法
1.JSONP
我们在script标签里面写JS代码,向一个不符合同源策略的端口发送接口请求可能会出现跨域问题,但是如果我们这样
<script src="https://github.com/vuejs/core"> //Vue3 源码地址
这样就可以引入Vue3里面的东西了
写在script标签里面写代码,会发生跨域问题,直接向这样
<script src="https://github.com/vuejs/core"> //Vue3 源码地址
就不用考虑同源策略问题了
思考
我们是不是可以在接口请求里面动态创建script 标签,这样是不是就不用考虑跨域问题了?
JSON:其实JSONP解决跨域问题的原理就是这样,JSONP 利用了 <script>
标签不受同源策略限制的特点,通过动态插入 <script>
标签来发起跨域请求,并通过回调函数接收和处理数据。这种方法仅适用于 GET 请求,因为 <script>
标签只支持 GET 方法。
前端示例代码
-
创建回调函数:
- 在前端页面中定义一个全局函数,该函数将被用来处理服务器返回的数据。
-
构建请求URL:
- 构建一个URL,其中包含回调函数的名字作为查询参数的一部分。
-
动态插入
<script>
标签:- 创建一个
<script>
标签,并将其src
属性设置为上述URL。 - 将
<script>
标签插入到DOM中,浏览器将自动发起请求。
- 创建一个
-
服务器响应:
- 服务器接收到请求后,返回一个包含回调函数名称和数据的JavaScript代码片段。
-
执行回调函数:
- 浏览器执行从服务器返回的JavaScript代码,从而调用之前定义的回调函数,并将数据作为参数传递给该函数。
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP Example</title>
</head>
<body>
<script>
function handleResponse(data) {
console.log('Data received:', data);
}
const Url = 'http://api.example.com/data?callback=handleResponse';
const scriptTag = document.createElement('script');
scriptTag.src = Url;
document.head.appendChild(scriptTag);
</script>
</body>
</html>
后端
const express = require('express');
const app = express();
app.get('/data', (req, res) => {
const callback = req.query.callback; // 获取回调函数名
const data = { message: 'Hello from the server!' };
// 构造 JSONP 响应
const jsonpResponse = `${callback}(${JSON.stringify(data)})`;
res.setHeader('Content-Type', 'application/javascript');
res.send(jsonpResponse);
});
app.listen(8080, () => {
console.log('Server is running on port 8080');
});
为什么要向后端传一个callback 函数体?
-
动态生成 JavaScript 代码:
- 服务器需要知道客户端期望的回调函数名称,以便能够在返回的响应中生成正确的 JavaScript 代码片段。服务器将数据包装在这个回调函数的调用中返回。
-
执行回调函数:
- 当浏览器加载
<script>
标签时,它会执行从服务器返回的 JavaScript 代码。这包括调用客户端定义的回调函数,并将数据作为参数传递给该函数。
- 当浏览器加载
!!!!JSONP 只能适用于Get 请求
2.Cors
第一步:安装cors
npm install cors
第二步: 引入 Cors 中间件
const express = require('express');
const cors = require('cors');
const app = express();
`
第三步: 引入 Cors中间件
现在您需要配置 cors
中间件。根据您的需求,可以选择以下几种配置方式之一:
允许所有源
如果您希望允许所有源进行跨域请求,可以简单地调用 cors()
函数,如下所示:
app.use(cors());
允许特定源
如果您只想允许特定的源进行跨域请求,可以使用 cors
的选项对象来指定这些源:
app.use(cors({
origin: 'http://your-allowed-origin.com'
}));
这里,origin
选项指定了一个具体的域名作为允许的源。
设置其他CORS头部
您还可以设置其他CORS相关的HTTP头部,例如允许的HTTP方法、允许的头部等。例如:
app.use(cors({
origin: 'http://your-allowed-origin.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'], // 允许的HTTP方法
allowedHeaders: ['Content-Type', 'Authorization'], // 允许的头部
credentials: true, // 是否允许携带cookies
maxAge: 86400 // 预检请求的有效期(单位:秒)
}));
3.WebSocket
WebSocket 是一种网络通信协议,它提供了一个全双工的通信通道,可以在客户端和服务器之间进行双向数据传输。在使用 WebSocket 时,浏览器会先发起一个 HTTP 升级请求到服务器,请求将连接从 HTTP 协议升级为 WebSocket 协议。一旦连接建立成功,就可以绕过浏览器的同源策略(Same-Origin Policy)限制,因此在某种程度上可以解决跨域问题。
// 创建一个新的 WebSocket 连接
var socket = new WebSocket('ws://example.com/ws');
// 连接成功后触发
socket.onopen = function (event) {
console.log("Connection opened");
};
//连接关闭后触发
socket.onclose = function (event) {
console.log("Connection closed");
};
//收到消息后触发
socket.onmessage = function (event) {
console.log("Received: " + event.data);
};
WebSocket 的工作原理
-
握手:
- 客户端发起一个 HTTP 升级请求到服务器,请求将连接从 HTTP 协议升级为 WebSocket 协议。
- 服务器响应这个请求,确认升级到 WebSocket 协议。
- 一旦握手成功,连接就从 HTTP 升级到了 WebSocket,此时可以开始发送和接收数据。
-
数据传输:
- WebSocket 连接建立后,客户端和服务器都可以主动发送数据。
- 数据是以帧的形式发送的,可以是文本或二进制数据。
-
关闭连接:
- 任何一方都可以关闭连接。
- 关闭连接通常涉及发送一个特殊的关闭帧。
使用 WebSocket 解决跨域问题
由于 WebSocket 的设计方式,它天然支持跨域通信。这意味着,一旦客户端和服务器完成了握手过程,就可以绕过浏览器的同源策略限制,实现跨域的数据传输。
4.代理
创建或修改 vue.config.js
文件
首先,你需要在项目的根目录下创建一个名为 vue.config.js
的文件,如果已经有这个文件,可以直接编辑它。这个文件用于配置 Vue CLI 的构建行为。
步骤 2: 配置代理
在 vue.config.js
文件中,你需要定义一个 devServer
对象,其中包含代理规则。以下是一个简单的代理配置示例:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://example.com', // 你要代理的目标服务器地址
changeOrigin: true, // 如果目标服务器验证主机名,则需要此选项
pathRewrite: { // 重写路径
'^/api': '' // 移除路径中的 /api
}
}
}
}
}
详解配置项
target
:这是你希望代理请求到的目标服务器地址。可以是完整的 URL(包括协议头)。changeOrigin
:默认情况下,代理服务器会保留原始请求的主机头。如果目标服务器验证主机名,那么你需要设置changeOrigin: true
,这样代理服务器会更改请求头中的主机头,使其看起来像是直接从代理服务器发出的请求。pathRewrite
:这是一个对象,用于重写代理请求的路径。例如,在上面的例子中,所有的/api
路径都会被重写为空字符串,即移除/api
。