解决跨域问题

跨域是浏览器受同源策略的限制,同源策略是浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制(如发送 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.借助脚手架搭建服务器

常见的脚手架工具如vitewebpack等,都提供了代理配置选项,可以方便地配置代理服务器。

但是需要注意的是,这个跨域只针对开发环境有效,一旦打包之后,前端配置的跨域就不起作用了,打包后就必须部署在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下载地址

  1. 在根目录下的conf目录下找到配置文件nginx.conf并打开
  2. 在root属性修改为我们的静态页面的根路径,
  3. 增加反向代理配置。
  4. 修改端口号(可改可不改),默认是80,我这里修改成6789。
  5. 双击根目录下的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

相关推荐
除了菜一无所有!5 分钟前
基于SpringBoot技术的教务管理
java·spring boot·后端
混迹网络的权某7 分钟前
每天一道C语言精选编程题之求数字的每⼀位之和
c语言·开发语言·考研·算法·改行学it·1024程序员节
逸狼1 小时前
【JavaEE初阶】网络原理(2)
java·网络·java-ee
DEARM LINER2 小时前
mysql 巧妙的索引
数据库·spring boot·后端·mysql
梅洪2 小时前
005 IP地址的分类
网络
梅洪2 小时前
006划分子网
网络
IronmanJay3 小时前
【LeetCode每日一题】——862.和至少为 K 的最短子数组
数据结构·算法·leetcode·前缀和·双端队列·1024程序员节·和至少为 k 的最短子数组
码农幻想梦3 小时前
实验九 视图的使用
前端·数据库·oracle
加载中loading...4 小时前
Linux线程安全(二)条件变量实现线程同步
linux·运维·服务器·c语言·1024程序员节
安科瑞刘鸿鹏4 小时前
校园建筑用电安全监测装置 电气火灾监测预防设备功能介绍
运维·服务器·网络·嵌入式硬件·安全·能源