Last updated on

利用沙箱解决跨域问题

搞一个跨域场景

/etc/hosts

127.0.0.1 a.local.com
127.0.0.1 b.cors.com
const allowedOrigins = ["http://b.cors.com:3002"];

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  let cnt = 0;
  app.enableCors({
    origin: (origin, callback) => {
      console.log("origin: ", origin, cnt++);
      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
        return;
      }

      callback(null, false);
    },
  });

  // 托管静态文件
  app.useStaticAssets(join(__dirname, "..", "static", "father"), {
    prefix: "/a",
  });
  app.useStaticAssets(join(__dirname, "..", "static", "child"), {
    prefix: "/b",
  });

  await app.listen(3002);
}

cors 仅仅 让 b.cors.com 过

image image

Father 和 Child iframe 通信

The contentWindow property returns the Window object of an HTMLIFrameElement.

postMessage(message, targetOrigin);
const iframe = document.getElementById("childIframe");
document.getElementById("sendToChild").addEventListener("click", async () => {
  iframe.contentWindow.postMessage(
    {
      source: "XxxUniqueID",
      data: 66,
    },
    "*"
  );
});

window.addEventListener("message", async (event) => {
  if (event.data?.source === "XxxUniqueID") {
    console.log("[Father] Received message from parent:", event.data.data);
  }
});
window.addEventListener("message", async (event) => {
  if (event.data?.source === "XxxUniqueID") {
    console.log("[Child] Received message from parent:", event.data.data);

    window.parent.postMessage(
      { source: "XxxUniqueID", data: "Hello from Child!" },
      "*"
    );
  }
});

image

Father 让 Child 发请求,解决跨域问题

document
  .getElementById("requestButtonUseIframe")
  .addEventListener("click", async () => {
    // 利用 iframe 发送请求
    iframe.contentWindow.postMessage(
      {
        source: "XxxUniqueID",
        type: "Request",
        data: {
          endpoint: "http://localhost:3002/",
        },
      },
      "*"
    );
  });
window.addEventListener("message", async (event) => {
  if (event.data?.source !== "XxxUniqueID") {
    return;
  }
  const { type, data: eventData } = event.data;
  console.log("[Child] Received message from parent:", event.data.data);

  switch (type) {
    case "Request":
      {
        try {
          const res = await fetch(eventData.endpoint, {
            method: "GET",
          });
          const data = await res.text();
          console.log("[Child] Response:", data);

          window.parent.postMessage({ source: "XxxUniqueID", data }, "*");
        } catch (error) {
          console.error("[Child] Error:", error);
        }
      }
      break;

    default:
      break;
  }
});

image

目前基本流程跑通了

还有问题

  1. 发多个请求,咋知道拿到的结果是哪个请求对应的
  2. 如何在 father 里无感的调用用 api,屏蔽下层 postMessage 的实现细节

解法

  1. 生成 uuid
  2. 通过 uuid 和 EventEmitter 来进行组合实现
    1. 发布订阅模式 与 观察者模式 image