Safari web extensions CORSエラー回避してAPIリクエスト

こんにちは、技術部の Y です。


はじめに

Safari web extensions (以降Safari拡張機能)を開発する機会があり、その中でAPIサーバへのリクエストが必要だったのでその方法の備忘録です。

開発の背景としては以下のような状況でした。

  • Safari拡張機能から自社APIサーバにリクエストをしたい。
  • APIサーバは、自社のフロントエンドからのみリクエストを許可するようにAccess-Control-Allow-Originを設定している。

Safari拡張機能の作成の仕方、概念は以下のサイトを参考にさせていただきました。

manifestのバージョンは3です。

https://developer.apple.com/documentation/safariservices/safari_web_extensions/creating_a_safari_web_extension https://zenn.dev/h1d3mun3/articles/0bbcdcef81223c https://qiita.com/oreo/items/c2c99c6fed7bec18c609 https://qiita.com/syu3/items/77ebb4e6193ec441a88d

※この記事ではSafari拡張機能に関する詳しい説明や、CORSに関する説明はしていません。


結論

  1. manifest.json の host_permissions にドメインを設定する。
  2. background.jsからリクエストする。
				
					json:manifest.json
// 一部抜粋
    "host_permissions" : [
        "*://api.example.com/*"
    ]

				
			


補足

まず、Safari拡張機能の開発においてJSを実行できる箇所は以下の3箇所になります。

  • ポップアップ領域 (popup.js)
    Safariメニューから拡張機能を表示した際に実行される。

  • コンテントスクリプト (content.js)
    指定したURLのWEBページに対してスクリプトを挿入できる。 DOM API を使用してページのコンテンツを読み取り、変更することができる。

  • サービスワーカー
    (background.js) iosでSafari拡張をONにしている間、バックグラウンドで実行される。

※ popup.js、content.js、background.jsはSafari拡張機能の雛形作成時にデフォルトで作成されるファイル名です。manifest.jsonに定義してあげれば別のファイル名を使用することができます。


popup.jsからリクエストするとCORSエラーになりました。 Safari拡張にドメインのようなものが割り振られているみたいです。

				
					js:popup.js
console.log("Hello World!", browser);

const fetchData = async () => {
  const res = await fetch('https://api.example.com');
  const data = await res.json();
  return data;
}

setTimeout(async () => {
  const data = await fetchData();
  console.log(data); // Origin safari-web-extension://xxxxxxxx-0000-1111-2222-xxxxxxxxxxxx is not allowed by Access-Control-Allow-Origin. 
}, 10000) // 開発者ツールのコンソールでログを見るために10秒後にAPIリクエストするようにしてます。

				
			

content.js で実行

content.jsにからリクエストした場合もCORSエラーになりました。 スクリプトを挿入したWEBページからのリクエストとみなされています。

				
					js:content.js
browser.runtime.sendMessage({ greeting: "hello" }).then(async (response) => {
  console.log("Received response: ", response);
  const data = await fetchData();
  console.log(data); // Origin https://example.com is not allowed by Access-Control-Allow-Origin
});

const fetchData = async () => {
  const res = await fetch('https://api.example.com');
  const data = await res.json();
  return data;
}

				
			


background.js で実行

manifest.jsonでAPIサーバのドメインを指定して、background.jsに処理を書いたところ正常にリクエストすることができました。

				
					js:background.js
browser.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  console.log("Received request: ", request);
  const data = await fetchData();
  console.log(data); // json object is displayed.

  if (request.greeting === "hello")
      sendResponse({ farewell: "goodbye" });
});

const fetchData = async () => {
  const res = await fetch('https://api.example.com');
  const data = await res.json();
  return data;
}

				
			

background.jsが起動している(であろう)サービスワーカーに関して あまり理解できていないですが、WEBページの裏側で動かせるイベント駆動のJavaScript環境のようです。 実行される環境が通常のWEBページと異なるためCORSの影響は受けない。程度の認識です。 正確な説明ができる方ご教授ください。


補足2

サービスワーカーではXMLHttpRequestは使用できないみたいです。

実際に使用を試したところ以下のエラーが出力されました。

				
					js
const xhr = new XMLHttpRequest(); //ReferenceError: Can't find variable: XMLHttpRequest

				
			

スーパーソフトウエアの採用情報

あなたが活躍できるフィールドと充実した育成環境があります

blank
blank