跨域是浏览器受同源策略的限制,同源策略是浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制(如发送 ajax 请求,操作 dom,读取 cookie)。
最常见的影响就是发送 ajax 请求,本文着重讲这部分。
我们将ajax 请求地址 称为目标源 ,将当前页面地址称为 称为所处源 。源=协议+域名+端口,所处源和目标源的协议、域名、端口都相同才是同源,否则就是非同源,即跨域。
注意:
1.跨域限制仅存在浏览器端,服务端不存在跨域限制。
2.即使跨域了,网络请求也可以正常发出,但响应数据不会交给开发者。
3.link、script、img... 这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响。
解决方式:1.配置CORS
2.配置代理服务器
3.使用JSONP
一、配置CORS
CORS 全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照 CORS 规范,添加特定响应头来控制浏览器校验,大致规则如下:
● 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过。
● 服务器明确表示允许跨域请求,则浏览器校验通过。
tips:使用 CORS 解决跨域是最正统的方式,且要求服务器是"自己人"。
这里用express 框架写了一个接口,地址为http://localhost:3456
js
app.get("/api/data", (req, res) => {
// 模拟数据
const data = {
name: "John Doe",
age: 30,
city: "New York",
};
res.json(data);
});
我们将静态页面也放在http://localhost:3456
js
// 路由设置,部署前端静态页面
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
此时打开http://localhost:3456,然后发起请求http://localhost:3456/api/data,可以看到能正常获取数据。
然后我再使用插件Open with Live Server
打开页面,地址为http://localhost:5501。
此时在发起请求http://localhost:3456/api/data,可以看到因为跨域问题获取数据失败,但其实请求是成功发送出去的。
在解决跨域问题之前,我们先来了解一下简单请求和复杂请求。
1.简单请求解决跨域问题
对于简单请求,我们可以在服务器端设置响应头Access-Control-Allow-Origin
,允许跨域请求。
Access-Control-Allow-Origin
就像设置白名单一样,只有白名单中的源才能访问资源。可以设置为某个源,也可以使用*
设置所有源。
js
app.get("/api/data", (req, res) => {
// 允许所有源发起跨域请求
// res.setHeader("Access-Control-Allow-Origin", "*");
// 允许指定源发起跨域请求(只能指定一个)
res.setHeader('Access-Control-Allow-Origin','http://localhost:5501')
// 模拟数据
const data = {
name: "John Doe",
age: 30,
city: "New York",
};
res.json(data);
});
配置了Access-Control-Allow-Origin
后,我们就可以正常获取数据了。
2.复杂请求解决跨域问题
复杂请求会在发送请求之前,先发送一个预检请求(preflight request),预检请求是一个 OPTIONS 请求,用于询问服务器是否允许跨域请求。
因此我们需要配置一个options请求处理预检请求。
js
// 处理预检请求
app.options("/api/data", (req, res) => {
// 设置允许的跨域请求源
res.setHeader("Access-Control-Allow-Origin", "*");
// 设置允许的请求方法
res.setHeader("Access-Control-Allow-Methods", "GET");
// 设置允许的请求头
res.setHeader("Access-Control-Allow-Headers", "Authorization");
// 设置预检请求的缓存时间(可选)
res.setHeader("Access-Control-Max-Age", 7200);
// 发送响应
res.send();
});
app.get("/api/data", (req, res) => {
// 允许所有源发起跨域请求
res.setHeader("Access-Control-Allow-Origin", "*");
// 模拟数据
const data = {
name: "John Doe",
age: 30,
city: "New York",
};
res.json(data);
});
二、配置代理服务器
1.自己配置代理服务器
安装库npm i http-proxy-middleware
js
const { createProxyMiddleware } = require("http-proxy-middleware");
// 所有带/api前缀的请求都代理到localhost:3456
app.use(
"/api",
createProxyMiddleware({
target: `http://localhost:${PORT}`,
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
})
);
2.借助脚手架搭建服务器
常见的脚手架工具如vite
、webpack
等,都提供了代理配置选项,可以方便地配置代理服务器。
但是需要注意的是,这个跨域只针对开发环境有效,一旦打包之后,前端配置的跨域就不起作用了,打包后就必须部署在web服务器上,脱离了 vue的代理配置。
以vite为例,在vite.config.js
中配置反向代理如下
js
export default defineConfig({
// 配置服务器的代理设置
server: {
// 代理配置,用于重定向请求到其他服务器
proxy: {
// 定义一个代理规则,将/api路径下的请求代理到指定的目标服务器
'/api': {
// 目标服务器的地址
target: 'http://localhost:3456',
// 更改请求的origin为代理服务器的origin,以便与目标服务器交互
changeOrigin: true,
// 重写请求路径,移除/api前缀
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
3.使用 Nginx 搭建代理服务器
下载nginx下载地址
- 在根目录下的
conf
目录下找到配置文件nginx.conf
并打开 - 在root属性修改为我们的静态页面的根路径,
- 增加反向代理配置。
- 修改端口号(可改可不改),默认是80,我这里修改成6789。
- 双击根目录下的
nginx.exe
启动。
打开http://localhost:6789,发起请求http://localhost:3456/api/data,
三、JSONP
JSONP 是一种非官方的跨域解决方案,它利用了 script 标签没有跨域限制的特点,通过 script 标签的 src 属性发送请求,服务器返回一个函数调用,函数的参数就是返回的数据。JSONP 只能发送 GET 请求,且需要服务器配合返回数据。
有空再写
所有源代码都放到gitee仓库里了。gitee地址
参考资料:https://www.yuque.com/tianyu-coder/openshare/aksmvpbebgw7savk