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に関する説明はしていません。
結論
- manifest.json の host_permissions にドメインを設定する。
- 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 で実行
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
関連記事
- 2023-12-21
- テクノロジー