3、预检请求的优化
复杂请求会发预检请求, 相当于每个接口会发两次请求, 比较消耗资源, 那么是可以对预检请求进行优化, 可以采用以下两种方式
res.header("Cache-Control", "max-age=3600"); // 设置1小时缓存 1小时内不再发送预检请求
- 优化
OPTIONS
请求大小, 简化返回的内容
if (req.method === "OPTIONS") { return res.sendStatus(200); }
4、跨域时针对cookie问题的解决与设置
跨域时,其实默认是不会携带 cookie
的我们可以手动在客户端,手动加一下 cookie, 然后发现请求头中是不会携带的
解决跨域携带 cookie 也比较简单, 前后端都需要设置
前端: 在请求方法中添加携带凭证 ,不同的请求方式,添加方式也不一样, 具体方法如下:
// fetch 中设置 fetch('url',{ method:'GET', credentials:'include' }) // axios 中设置 axios.get('xxx', { withCredentials: true }) // ajax var xhr = new XMLHttpRequests(); xhr.open('GET','xxxxxx', true ) xhr.withCredentials = true //...
- 后端:
res.header("Access-Control-Allow-Credentials", true);
- 以上基本就是关于的CORS的相关内容了
三、PostMessage
postMessage 是用于不同窗口或框架之间进行安全通信的 API, 提供了简单灵活的方式来发送和接收数据
1、基本原理
- 发送消息的窗口,调用
postMessage
方法来像接收方发送消息(发送的消息和窗口源) - 接收消息的窗口内, 添加事件监听来自其他窗口的消息,通过
window.addEventListener('message',callbackFn)
通过callbackFn
回调函数接收 - 通过
event.origin
属性验证消息来源。 通过与跨域地址的页面进行收发消息, 间接达成与跨域请求
2、实战
1、我们先创建一个 A 页面 , 通过 Live Sever 起一个静态服务, 端口 5501 , A页面代码如下
<!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> <iframe id="iframe" src="http://localhost:4000/b.html" onload="loaded()" hidden ></iframe> <button onclick="request({ url: '/list'});">请求列表</button> </body> <script> const bwindow = document.getElementById("iframe").contentWindow; window.addEventListener("message", function (e) { if (e.origin !== "http://localhost:4000") return; console.log(e.data, e, "监听b页面给我发来的消息"); if (e.data.url === "/users") { console.log("users:", e.data); } if (e.data.url === "/list") { console.log("users:", e.data); } }); function loaded() { request({ url: "/users", }); } function request(data) { // 向bwindow发消息 bwindow.postMessage(data, "http://localhost:4000"); } </script> </html>
- 在A页面我们通过隐藏一个 iframe 内嵌一个 端口为 4000 的页面 B, 通过iframe 的 load 方法, 利用iframe的 发送 postMessage 请求到 B页面
2、创建 B 页面 监听 A页面发送过来的消息, 代码如下
<!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> B页面 <script> window.addEventListener("message", async function (e) { if (e.origin === "http://127.0.0.1:5501") { console.log("监听a页面发来的消息", e.origin, e.data, e.source); const response = await fetch(e.data.url, e.data); const result = await response.json(); e.source.postMessage( { url: e.data.url, data: result, }, e.origin ); } }); </script> </body> </html>
- 我们通过B页面接收 来自
"http://127.0.0.1:5501"
源发来的请求, 然后B页面通过 express 起一个服务, 端口为 4000, B页面由这个服务提供, 那么B页面和 服务端交互将不存在跨域问题, 然后我们在B页面发起请求B服务器上的资源,请求完成后,在通过e.source.postMessage
将数据发送回A页面, 下面是B的服务端代码,起一个 4000 的服务, 然后打开B页面http://localhost:4000/B.html
const express = require("express"); const app = express(); app.use(express.static("public")); // 404 错误处理中间件 const whiteList = ["http://127.0.0.1:5501"]; app.get("/users", (req, res) => { res.json([{ id: 1, name: "zhangsan" }]); }); app.get("/list", (req, res) => { res.json([ { id: 1, list: "1" }, { id: 2, list: "2" }, ]); }); app.listen(4000, () => { console.log("sever 4000"); });
- 这样我们就完成了 , A页面请求 B服务,会跨域, 但是 A 通过 B页面去发送请求到 B 服务即可解决跨域这一问题。
总结
本文通过讲解了什么是跨域, 并对两种跨域的方式进行了,代码演示, 并用来解释原理, JSONP是很早的解决跨域的方式, 他利用了script请求可以绕开跨域限制的原理, 但是这种方式只支持GET请求, 并且是不够安全的,容易发生 JSONP 漏洞很容易被别人利用, 如果非要使用最好加上校验 refer的白名单, CORS 解决跨域问题是比较正统的实现, 基本都是后端为主,只是是否携带cookie时需要前端配合, 并且跨域情况下, 请求上也分简单请求和复杂请求, 介绍了复杂请求下的问题解决与策略,并且简单介绍了预检请求的优化和跨域情况下 cookie 的携带设置。 postMessage 原理是A页面想要请求 B服务但是会跨域,但是A页面可以跟B页面通讯,然后让B页面发送请求给B服务, 这样就可以解决跨域问题。为此我们还封装了一下, 通过A页面传递请求参数路径,让B页面发起请求与中转数据, 大家有兴趣的可以试试内嵌掘金这个网站试试能不能通过上述方法能不能请求到掘金服务器。 最后, 单纯只看概念,背面试题,其实无法真正了解,纸上得来终觉浅 绝知此事要躬行, 只有自己手敲实现过, 才能对其原理理解更加深刻。