跨域的概念不再解释,直接演示下出现跨域的情况:
前端代码(index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CORS Example</title>
</head>
<body>
<h1>CORS Example</h1>
<button id="fetchData">Fetch Data</button>
<p id="result"></p>
<script>
document.getElementById('fetchData').addEventListener('click', () => {
fetch('http://localhost:4000/data')
.then(response => response.json())
.then(data => {
document.getElementById('result').innerText = JSON.stringify(data);
})
.catch(error => {
console.error('Error:', error);
});
});
</script>
</body>
</html>
点击Fetch Data
按钮,向http://localhost:4000/data
请求数据
后端代码(server.js):
const express = require("express");
const app = express();
const port = 4000;
app.get("/data", (req, res) => {
res.json({ message: "This is some data from the server." });
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
首先我们直接启动服务器:
node server.js
打开浏览器直接输入http://localhost:4000/data
回车,可以返回数据,而且没有任何错误
此时,我们通过vs code 中的插件 live server打开index.html,这种方式的含义是,本地启动一个开发服务器,域名为http://127.0.0.1:5501
,当前位置为http://127.0.0.1:5501/index.html
Access to fetch at ‘http://localhost:4000/data’ from origin ‘http://127.0.0.1:5501’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
点击按钮时,报错,意思是说,我们当前所在域名为http://127.0.0.1:5501
,去请求了一个非同源的资源http://localhost:4000/data
,因为是非同源的,出现了跨域情况,这时候被浏览器拦截了,可以发现响应的状态码是200,ok 的
解决:
通过手动设置Access-Control-Allow-Origin和其他CORS相关的头部来解决跨域问题。
后端代码(server.js)
const express = require('express');
const app = express();
const port = 4000;
app.get('/data', (req, res) => {
// 设置CORS头部
res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有来源(跨域)
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); // 允许的HTTP方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // 允许的头部
res.json({ message: 'This is some data from the server.' });
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
我们针对要访问的资源路径 http://localhost:4000/data
,配置该资源对于跨域请求的限制。
Access-Control-Allow-Origin:
表示的是可以被来自哪些域的请求访问到。
该头部指定了允许跨域访问的域。可以设置为特定的域名(比如本例中http://localhost:5501),或使用*
表示允许任何域名访问。Access-Control-Allow-Methods:
该头部指定了允许的HTTP方法。例如,可以允许GET、POST、PUT、DELETE等方法。Access-Control-Allow-Headers:
该头部指定了前端在请求中允许使用的自定义头部字段。例如,如果请求中包含Content-Type或Authorization头部,则需要在这里指定。
之后,我们就可以访问到该资源
处理预检请求(OPTIONS)
某些请求(如带有自定义头部的POST请求)会触发浏览器的“预检请求”(OPTIONS方法)。预检请求的作用是让浏览器先确认服务器是否允许跨域访问。
简单请求:
请求方法为:GET、POST、HEAD,请求头 Content-Type 为:text/plain、multipart/form-data、application/x-www-form-urlencoded 的就属于 “简单请求” 不会触发 CORS 预检请求。
非简单请求:
使用的HTTP方法超出了GET、POST、HEAD,如PUT、DELETE等,请求头的 Content-Type 为 application/json 就会触发 CORS 预检请求,这里也会称为 “非简单请求”。
特点:
在实际请求发送之前,浏览器会自动发起一个OPTIONS请求,这就是所谓的预检请求。预检请求用来向服务器询问实际请求是否被允许。
服务器必须正确响应预检请求:服务器需要在预检请求中返回正确的CORS头部,以允许实际请求的发送。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Non-Simple Request Example</title>
</head>
<body>
<h1>Non-Simple Request Example</h1>
<button id="sendRequest">Send Non-Simple Request</button>
<p id="response"></p>
<script>
document.getElementById('sendRequest').addEventListener('click', () => {
fetch('http://localhost:4000/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer some-token' // 自定义头部
},
body: JSON.stringify({ key: 'value' }) // 请求体
})
.then(response => response.json())
.then(data => {
document.getElementById('response').innerText = JSON.stringify(data);
})
.catch(error => {
console.error('Error:', error);
});
});
</script>
</body>
</html>
后端:
const express = require('express');
const app = express();
const port = 4000;
app.use(express.json()); // 解析JSON请求体
// 处理预检请求,设置CORS头部并返回204 No Content状态码,表明服务器允许实际请求的发送。
app.options('/data', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(204); // 204 No Content
});
// 处理实际的POST请求,并返回JSON数据
app.post('/data', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.json({ message: 'Data received successfully', receivedData: req.body });
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
在点击按钮之后,可以发现发送了两次网络请求,第一次为预检请求,第二次为post请求
options请求: