跨域是浏览器受同源策略的限制,同源策略是浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制(如发送 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
app.get("/api/data", (req, res) => {// 模拟数据const data = {name: "John Doe",age: 30,city: "New York",};res.json(data);
});
我们将静态页面也放在http://localhost:3456
// 路由设置,部署前端静态页面
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
就像设置白名单一样,只有白名单中的源才能访问资源。可以设置为某个源,也可以使用*
设置所有源。
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请求处理预检请求。
// 处理预检请求
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
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
中配置反向代理如下
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 请求,且需要服务器配合返回数据。
有空再写
参考资料:https://www.yuque.com/tianyu-coder/openshare/aksmvpbebgw7savk