【从原理到实战】彻底搞懂跨域问题 (一)(1)

简介: 前言什么是跨域: 浏览器为了安全性,设置同源策略导致的, 或者说是一种浏览器的限制同源策略: 是一种约定,WEB 应用只能请求同一个源的资源什么时候会跨域: 协议名、域名、端口号 不同本文将从原理, 到最简代码实现, 演示解决跨域的方法和流程,纸上得来终觉浅 绝知此事要躬行, 只有自己手敲实现过, 才能对其原理理解更加深刻。

前言

什么是跨域: 浏览器为了安全性,设置同源策略导致的, 或者说是一种浏览器的限制同源策略: 是一种约定,WEB 应用只能请求同一个源的资源什么时候会跨域: 协议名、域名、端口号 不同

本文将从原理, 到最简代码实现, 演示解决跨域的方法和流程,纸上得来终觉浅 绝知此事要躬行, 只有自己手敲实现过, 才能对其原理理解更加深刻。

一、JSONP

1、原理

JSONP是利用 html 中的script标签没有跨域访问限制的来实现的 工作原理:

  • 客户端通过script标签向服务器发送请求, 同时定义好回调函数接收 <script src="http://example.com/api?callback=handleResponse"></script>
  • 服务器接收到请求后,会把数据进行填充,并且放回回调函数中,例如 handleResponse({data:'response'})
  • 客户端在页面中定义相应的回调函数 handleResponse, 服务器返回的脚本会被浏览器执行, 从而触发回调函数,处理服务器返回的数据(把数据作为参数传给客户端定义的函数并执行), 这样就实现了跨域请求和数据获取

局限: JSONP 的限制是需要通过<script> 标签加载外部脚本, 但是它有一些局限性

JSONP 只支持 GET 请求, 无法通过 POST 或 其他类型请求 JSONP 只能接收 纯文本数据,无法处理 JSON 对象 或 XML 数据

2、实站(最简化代码)

接下来就通过最简代码的方式, 实现JSONP的原理

客户端 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JSONP</title>
  </head>
  <body>
    JSONP
  </body>
  <script>
    // 像页面中添加一个script标签, 通过script标签进行访问, 返回一个可执行的 函数
    function sendJsonp() {
      const callbackName = `jsonpFn_${Date.now()}`; // 为了防止缓存 加一个时间戳
      // 发送请求前先将函数名放到window上, 赋值一个函数, 当后端收到请求后,会返回一个该函数执行的脚本, 并将函数的参数传入到该函数中
      window[callbackName] = function (data) {
        console.log(data, ".datadatadatadata");
      };
      // 将该函数拼成一个url地址请求到后端
      const url = `http://localhost:4000/jsonp?q=1&w=2&jsonp=${callbackName}`;
      const script = document.createElement("script");
      script.src = url;
      // 将脚本标签添加到head中,发送请求
      document.head.appendChild(script);
    }
    sendJsonp();
  </script>
</html>

服务端代码

const express = require("express");
const app = express();
app.get("/jsonp", (req, res) => {
  // 查询参数
  const { jsonp, q, w } = req.query;
  const result = {
    g: Array.from({ length: 10 }, (_, i) => ({ q: `${w}${i + 1}` })),
  };
  res.send(`${jsonp}(${JSON.stringify(result)})`);
});
app.listen(4000, () => {
  console.log("sever 4000");
});

解释: 从上面简化的代码中,可以看出, 客户端就是通过构建 script 标签, 通过src 的访问将函数名发送给服务端, 并且构建一个函数等待执行, 服务端收到后,拿着函数名在拼装成一个函数调用的字符串, 当客户端发送 script 请求时,会将结果进行执行, 因为函数挂载到了window上, 所以会执行我们之前构造好的函数, 并将data 这个参数传递进去,我们就拿到了结果

屏幕截图 2023-07-05 231756.png

二、CORS

1、概念

CORS 跨域资源共享:是一种浏览器机制,允许跨域请求共享资源,在Web开发中,当一个网页向另一个域名下的资源发起请求时,如果请求的目标域和当前域不同,就会设计到跨域问题, 默认情况下, 浏览器会限制这种跨域请求,以保护用户安全

2、工作流程 (可略)

CORS 跨域资源共享通过在服务端设置相应的响应头来解决跨域问题, 它使用一些特定的HTTP 头部和预检请求已实现跨域通信

  • 1、发出跨域请求: 在浏览器中的网页向其他域名下的资源发起跨域请求时, 浏览器会首先发送一个跨域请求
  • 2、发送预检请求(可选):对于某些类型的跨域请求,例如带有一些自定义的HTTP头部或特殊方法(PUT、DELETE)的请求,浏览器会发送一个OPTIONS请求,称为预检请求,用于向服务器验证跨域请求是否被允许
  • 3、服务器设置响应头: 服务器收到跨域请求后,可以设置响应头来告诉浏览器允许请求的源(Origin)、允许的 HTTP 方法、允许的自定义头部信息
  • Access-Control-Allow-Origin 指定允许访问域名,可以单个 通配符 列表
  • Access-Control-Allow-Methids 指定允许的 HTTP 方法
  • Access-Control-Allow-Headers 指定允许的自定义头
  • access-Control-Allow-Credentials 是否允许发送身份凭证 允许时 Origin不能为*
  • 4、处理跨域响应,如果服务器设置了响应头,浏览器会根据指定的规则处理

3、实战 ( 最简化代码 )

客户端代码

我们通过 livesever 先给页面起一个静态服务, 域名为  http://127.0.0.1:5501/HTTP/xxx/indexMini.html

indexMini.html 页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      async function getUsers() {
        const response = await fetch("http://localhost:3000/users", {
          method: "POST",
        });
        const data = await response.json();
        console.log(data);
      }
      getUsers();
    </script>
  </body>
</html>

服务端代码

const express = require("express");
const app = express();
app.use((req, res, next) => {
  next();
});
app.post("/users", (req, res) => {
  res.json([{ id: 1, name: "zhangsan11" }]);
});
app.listen(3000, () => {
  console.log("sever 3000");
});

解释: 上面用静态服务起一个客户端 端口 5501, 然后通过express 起一个后端服务 端口3000,  客户端刷新页面会发起一个/users的请求, 一定会发生跨域问题, 'no-cors' to fetch the resource with CORS disabled. 显示 no-cors 获取资源失败, 其实后端是收到了请求,也将数据返回去了, 但是接口的响应表头中没有设置允许的 源 Orign, 所以浏览器对于这种跨域又没有设置 Orign 的情况, 就会报错 阻止客户端收到返回值

1、 跨域问题的解决

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  next();
});

实际上只需要后端增加一行代码, 就可以解决跨域问题了, 因为 * 代表所有源都允许, 如果希望指定的源或者给几个指定源添加白名单的形式,可以这样写

const whiteList = ["http://127.0.0.1:5501"];
if (whiteList.includes(req.headers.origin)) {
    res.header("Access-Control-Allow-Origin", req.headers.origin);
}

这样就只能针对特定的源允许跨域请求了

2、针对复杂请求的相关处理与解决

上面我们解决完跨域问题了, 但是还存在几个问题, 我们的请求是post的,还没有添加header, 一旦修改请求方法,或者header增加一些字段,就发现有问题了,所以下面开始处理这些复杂请求 先简单区分什么是复杂请求什么是简单请求: 区分他们有两个指标, 一个是请求方法: HEAD、GET、POST是简单请求, header中常见的 Content-Type 是 application/x-www-form-urlencodedmultipart/form-data 和一些 AcceptAccept-LanguageContent-Language 这些都是简单请求, 简单请求就很简单,不需要在俄额外进行服务端的处理设置了, 可以尽情使用

除此之外,其他的请求方法 和 其他的请求头。都是复杂请求, 复杂请求会额外发送一个 OPTION 请求来询问服务端是否支持, 那么怎么才能支持呢? 就是要分别处理 这里格外需要注意, header 中的 Content-Type: "application/json 是复杂请求, 比如 POST 的时候 我们经常需要这种类型, 那么在跨域的时候就需要特殊处理了,否则会报错 Request header field content-type is not allowed  这种解决只需要在代码中增加一行

 res.header("Access-Control-Allow-Headers", "Content-Type");

如果想增加自定义的头 比如请求时候 再增加一个 custom: 3000

 headers: {
    "Content-Type": "application/json",
    custom: "3000",
 },

这样也只需要后端在允许的Headers中加一下

js

复制代码

 res.header("Access-Control-Allow-Headers", "Content-Type, custom");

相关文章
|
移动开发 资源调度 JavaScript
Vue 3.0 && Nginx 项目打包部署知识都在这了~
Vue 3.0 && Nginx 项目打包部署知识都在这了~
8014 1
Vue 3.0 && Nginx 项目打包部署知识都在这了~
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
221418 69
|
存储 Java Maven
maven在尝试访问nexus存储库时得到“未授权” Not authorized , ReasonPhrase:Unauthorized
maven在尝试访问nexus存储库时得到“未授权” Not authorized , ReasonPhrase:Unauthorized
1151 0
|
监控 测试技术 开发者
开发者如何使用微服务引擎MSE
【10月更文挑战第16天】开发者如何使用微服务引擎MSE
842 4
|
存储 运维 监控
首次!阿里云可观测技术论文登上两大国际顶会
首次!阿里云可观测技术论文登上两大国际顶会
341 1
|
前端开发 JavaScript 开发者
工程化(webpack+vite)
在现代前端开发中,工程化是提高开发效率和项目质量的关键。UniApp 结合 Webpack 和 Vite,提供强大的工程化支持。Webpack 功能强大,支持复杂项目的构建;Vite 则利用现代浏览器的 ESM 特性,提供快速的开发体验。开发者可根据项目需求选择合适的工具,显著提升开发效率和项目质量。
|
机器学习/深度学习 人工智能 自动驾驶
人工智能的伦理困境:机器的自主性与人类的责任
【8月更文挑战第8天】在人工智能技术飞速发展的今天,一个日益凸显的问题是关于AI的伦理困境。随着机器学习和深度学习技术的进步,AI系统展现出越来越高的自主性,这引发了关于人类责任和控制的哲学讨论。本文将探讨AI自主性的提升如何影响人类的伦理责任,以及我们应如何平衡技术进步与道德考量。
|
JavaScript API 数据库
ant design vue + jeecgboot 实现本地上传视频及播放视频功能
ant design vue + jeecgboot 实现本地上传视频及播放视频功能
875 2
|
开发工具 git 缓存
初识git:IntelliJ IDEA配置git步骤及如何将本地仓库项目上传至git仓库步骤详解
步骤一:访问git官网(https://gitforwindows.org/)下载所需git安装包。 步骤二:IDEA==》file==》setting==》如图①==》如图② 步骤三:在弹出界面搜索gitee插件下载并安装,安装步骤直接点击下一步直至安装成功即可。
5735 0
|
负载均衡 安全 网络协议
阿里云专有云网络基础了解1
阿里云专有云网络基础了解1
432 2