Web Workerのfetch APIで遭遇したCORS問題:ViteでのData URL問題とその解決方法
はじめに
画面のスクリーンショットを取得する機能を実装する際に、思わぬつまずきポイントに遭遇しました。
modern-screenshot
というライブラリを使用し、Web Workerで処理を行おうとしたところ、CORSエラーで詰まってしまいました。
この記事では、その問題の詳細と解決方法を共有したいと思います。
実装の背景
やりたかったこと
- Webアプリケーションで画面のスクリーンショットを取得する機能を実装
- パフォーマンスを考慮し、
modern-screenshot
ライブラリを使用 - 処理の重さを考慮し、Web Worker上でfetchする
modern-screenshotについて
modern-screenshot
は画面キャプチャのためのライブラリです。Web Workerに対応していて、
メインスレッドをブロックせずにfetchしてくれる機能があります。
このとき、対応するjsのURLをworkerURLとして関数に引き渡せば機能が動くようになっています。
最初の実装
コード
const workerUrl = new URL('modern-screenshot/worker', import.meta.url).href;
const blob = await domToBlob(testTarget.value, { workerUrl,
workerNumber: window.navigator.hardwareConcurrency,
type: 'image/png' });
検証環境では動作したが...
モックAPIを使用した検証環境では問題なく動作しました。しかし、実際の開発環境に組み込んでみると以下のようなエラーが発生:
Access to fetch at 'https://api.example.com/data' from origin 'null' has been blocked by CORS policy
問題の原因
調査の結果、以下の要因が重なって問題が発生していたことが分かりました:
- Viteの開発環境では、JavaScriptファイルがData URLとして読み込まれる
-
new URL(...).href
で取得したURLは、ViteによってData URL形式に変換される - Data URLのoriginは
null
として扱われる - Web Worker内でのfetch APIリクエストは、originが
null
のためCORSエラーとなる
ViteにはWorker
を直接扱う機能がありますが、modern-screenshot
のAPIはWorkerのURLを要求しているため、今回は使用できませんでした.
(それを使えれば、おそらくinline化されないので回避できると思います)
解決方法
Blobを使用して解決させました
const workerCode = await fetch(workerUrl).then(res => res.text());
const blob = new Blob([workerCode], { type: 'text/javascript' });
const blobUrl = URL.createObjectURL(blob);
const screenshot = new ModernScreenshot({
workerUrl: blobUrl,
});
まとめ
Data URLはCORSがnullになるので、気を付けよう。