少しハマりましたので、忘備録として。
参考サイト https://knmts.com/become-engineer-31/
この記事を書いてから、同目的の別記事を書いています。こちらの方がシンプルかもしれません。
https://i-say.net/note/1100/
以下の記事にある「mode: ‘no-cors’」の設定は、あまり今は推奨しません。
上記のページで解決できるので、上記のページを先にお読み下さい。
1. はじめに
Google Apps Script(GAS)を使用してGoogleスプレッドシートやその他のGoogle Appsと連携するアプリケーションを開発する際、フロントエンドからのリクエストがCORS(Cross-Origin Resource Sharing)ポリシーによってブロックされることがあります。これは、セキュリティ上の理由からブラウザが異なるオリジン間のリソース共有を制限するため発生します。本記事では、このCORSエラーを解決する方法を詳しく解説します。
2. CORSエラーの原因と基本的な理解
CORSエラーは、フロントエンドからGASへのリクエストが特定の条件を満たさない場合に発生します。GASは、application/json
形式のContent-Typeを持つプリフライトリクエストを処理できないため、この形式でリクエストを送るとCORSエラーが発生することがあります。
3. 解決策
- Content-Typeの変更:
application/json
ではなく、x-www-form-urlencoded
やtext/plain
を使用してリクエストを送信します。 - リクエストの形式の調整: リクエストボディを適切にエンコードし、GASが受け入れ可能な形式でデータを送信します。
- レスポンスの正しい解釈: GASからのレスポンスを適切に解釈し、200 OKが必ずしも成功を意味しないことを理解します。成功時は302 FOUNDが返され、
Access-Control-Allow-Origin
ヘッダーが設定されます。
4. 実践例
具体的なコード例を交えて、フロントエンドからGASへのリクエストの正しい送り方、GASでのリクエストの受け取り方、そしてフロントエンドへの適切なレスポンスの返し方を示します。
JavaScriptでのfetchメソッドの使用例:(フォーム入力でname,email値を受けている想定)
x-www-form-urlencodedの場合
document.getElementById('reservation-form').addEventListener('submit', function(e) {
e.preventDefault(); // フォームのデフォルトの送信を防止
// フォームからデータを取得
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
// GASのエンドポイントURL
const endPoint = 'https://script.google.com/macros/s/あなたのスクリプトのID/exec';
// リクエストのボディを `application/x-www-form-urlencoded` 形式で構築
const formData = new URLSearchParams();
formData.append('name', name);
formData.append('email', email);
fetch(endPoint, {
method: 'POST',
mode: 'no-cors', // CORSポリシーによる制限を回避
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text(); // ここではtext()を使用していますが、実際にはGASのレスポンスに合わせて適宜変更してください。
})
.then(data => {
console.log(data); // 成功した場合の処理
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
});
text/plainの例
document.getElementById('dataForm').addEventListener('submit', function(e) {
e.preventDefault(); // フォームの通常の送信を防止
// フォームからデータを取得
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
// GASのエンドポイントURL (あなたのウェブアプリのURLに置き換えてください)
const endPoint = 'https://script.google.com/macros/s/あなたのスクリプトのID/exec';
// リクエストのボディを構築
const data = `name=${name}&&email=${email}`;
fetch(endPoint, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: data
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text(); // 実際にはGASからのテキストレスポンスを受け取ります
})
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Error:', error);
});
});
受け手のGAS例 指定のgoogle Docに追加書き込みという想定
function doPost(e) {
// GoogleドキュメントのID (あなたのドキュメントに置き換えてください)
var docId = 'あなたのドキュメントID';
var doc = DocumentApp.openById(docId);
var body = doc.getBody();
// リクエストからデータを取得
var requestData = e.postData.contents;
var contentType = e.postData.type;
// コンテンツタイプに応じたデータの処理
if (contentType === 'application/x-www-form-urlencoded') {
// URLエンコードされたデータを解析
var data = parseUrlEncoded(requestData);
var textToInsert = '名前: ' + data.name + ', メールアドレス: ' + data.email;
} else if (contentType === 'text/plain') {
// テキストデータを直接使用
var parts = requestData.split('&&');
var textToInsert = '名前: ' + parts[0].split('=')[1] + ', メールアドレス: ' + parts[1].split('=')[1];
} else {
// サポートされていないコンテンツタイプ
return ContentService.createTextOutput('Unsupported content type: ' + contentType).setMimeType(ContentService.MimeType.TEXT);
}
// ドキュメントにテキストを追加
body.appendParagraph(textToInsert);
// 成功レスポンスを返す
return ContentService.createTextOutput('Data added to document successfully').setMimeType(ContentService.MimeType.TEXT);
}
// application/x-www-form-urlencoded形式のデータを解析する関数
function parseUrlEncoded(data) {
var result = {};
var pairs = data.split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
}
return result;
}
ちなみに、postをtext/plainで送り、内容をcontentで送信している場合は、以下のようにシンプルでいけました。
function doPost(e) {
var docId = ""; // Google ドキュメントの ID
var doc = DocumentApp.openById(docId);
var body = doc.getBody();
// 'content' パラメーターの値を取得
var content = e.postData.getDataAsString();
// ドキュメントに 'content' の値を追加
body.appendParagraph(content);
// 変更を保存
doc.saveAndClose();
var result = {
status: 'success',
message: 'データが正常に処理されました'
};
return ContentService.createTextOutput('Success');
}
5. まとめ
GASを使用する際にCORSエラーに直面した場合、上記の解決策を試すことで問題を解決できる可能性があります。重要なのは、リクエストとレスポンスの処理方法を適切に調整し、GASの仕様とブラウザのセキュリティポリシーの両方を理解することです。
この記事が、GASを使った開発におけるCORSエラーの解決に役立つことを願っています。
WEBプログム、WEBデザインなどの制作については、以下を御覧ください。
WEBプログム、WEBデザインなどの制作