質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2393閲覧

GAS で 外部APIを非同期実行し、返り値を実行元で確認したい

kiringiraffe

総合スコア4

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2023/08/30 21:21

編集2023/08/30 21:29

実現したいこと

こちらの流れにおいて (3) を実現したいです。

(1) GAS に NotionAPI でのページ作成処理を実装 → 実現できた
(2) その GAS を, 任意のサイトのフロントから fetch() で実行 → 実現できた
(3) 任意のサイトで GAS の返り値 ( NotionAPI の返り値 ) を確認 → これを実現したい

発生している問題・エラーメッセージ

任意のサイト ( https://example.com ) のコンソールでこちらの fetch() を実行すると、GAS は処理されて Notion に無事ページは作成されました。つまり (1) (2) はできたのです。( 実際はコンソールで実行するのではなくChrome拡張機能で「実行」ボタンを作ります。)

JavaScript

1const gasEndpointUrl = 'https://script.google.com/macros/s/●●●/exec'; 2 3fetch(gasEndpointUrl) 4 .then(response => response.text()) 5 .then(data => { 6 console.log(data); 7 }) 8 .catch(error => { 9 console.error('Error:', error); 10 });

しかし、任意のサイトのコンソールにこちらのエラーメッセージが表示され、(3) ができない状況です。

Access to fetch at 'https://script.google.com/macros/s/●●●/exec' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

該当のソースコード

こちらが GAS のコードです。async で NotionAPI の返り値を待って return しています。
Notion に無事ページは作成されましたし、35行目の console.log() も正常な NotionAPI の返り値でした。

JavaScript

1/*============================================== 2 3 NotionAPIでページを作成 4 - Notion に無事ページは作成されました 5 - 35行目の console.log() も正常な値でした 6 7==============================================*/ 8 9// NotionAPIキー 10const TOKEN = 'secret_▲▲▲'; 11 12// GASの実行 13async function doGet() 14{ 15 const mainResult = await main(); 16 return ContentService.createTextOutput(JSON.stringify(mainResult)); 17} 18 19/** 20 * メイン処理 21 */ 22async function main(){ 23 try { 24 25 // ページ作成 26 const databaseId = '■■■'; 27 const fetchedContent = await createPage(databaseId); 28 29 // Notion側でエラー 30 if (fetchedContent.object === 'error') { 31 throw ('Notion側でエラー'); 32 } 33 34 // 結果確認 35 console.log('fetchedContent: ', fetchedContent); 36 return fetchedContent; 37 38 } catch(e) { 39 console.log('e: ', e); 40 return e; 41 } 42} 43 44/** 45 * ページ作成 46 * マニュアル https://developers.notion.com/reference/post-page 47 */ 48async function createPage(databaseId) 49{ 50 const payload = { 51 "parent": { 52 "database_id": databaseId 53 }, 54 "properties": { 55 "Name": { 56 "title": [ 57 { 58 "text": { 59 "content": getCurrentDate() 60 } 61 } 62 ] 63 }, 64 } 65 }; 66 67 const options = { 68 'method': 'POST', 69 'headers': { 70 'content-type': 'application/json; charset=UTF-8', 71 'Authorization': 'Bearer ' + TOKEN, 72 'Notion-Version': '2022-06-28' 73 }, 74 'muteHttpExceptions': true, 75 'payload': JSON.stringify(payload) 76 } 77 const endpoint = 'https://api.notion.com/v1/pages'; 78 const fetchedContent = fetchUrl(endpoint, options, 'json'); 79 return fetchedContent; 80} 81 82/** 83 * fetch() を実行し getContentText() を返す 84 */ 85async function fetchUrl(url, options = {}, type = 'text') 86{ 87 return new Promise((resolve, reject) => { 88 const response = UrlFetchApp.fetch(url, options); 89 if (response.getResponseCode() === 200) { 90 const content = response.getContentText(); 91 resolve(type === 'json' ? JSON.parse(content) : content); 92 } else { 93 const mes = `Request failed with status code ${response.getResponseCode()}`; 94 reject(new Error(mes)); 95 } 96 }); 97} 98 99/** 100 * 現在日時を 2023-01-01 0:00 の形式で生成 101 * Notionに作成するページのタイトルに使う 102 */ 103function getCurrentDate() 104{ 105 const formatNumber = (number) => (number < 10 ? '0' + number : number); 106 107 const now = new Date(); 108 const year = now.getFullYear(); 109 const month = formatNumber(now.getMonth() + 1); 110 const day = formatNumber(now.getDate()); 111 const hours = formatNumber(now.getHours()); 112 const minutes = formatNumber(now.getMinutes()); 113 114 const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}`; 115 return formattedDateTime; 116}

試したこと [1]

fetch()の第二引数で'Content-Type'を以下のように指定しても、全く同じエラーメッセージでした。尚、Notion の方は上記と同様に無事ページは作成されました。

JavaScript

1const gasEndpointUrl = 'https://script.google.com/macros/s/●●●/exec'; 2 3fetch(gasEndpointUrl, { 4 method: 'GET', 5 headers: { 6 'Content-Type': 'application/x-www-form-urlencoded', 7 }, 8 }) 9 .then(response => response.text()) 10 .then(data => { 11 console.log(data); 12 }) 13 .catch(error => { 14 console.error('Error:', error); 15 });

試したこと [2]

また(これはエラーメッセージの CORS の件と関係ないかもしれませんが)、デプロイしたウェブアプリのURL 'https://script.google.com/macros/s/●●●/exec' に直接ブラウザアクセス ( ブラウザのURLバーに入力 ) すると、'スクリプトが完了しましたが、返された値はサポートされている戻り値の型ではありませんでした。' と表示されてしまい、NotionAPI の結果を確認することはできませんでした。

イメージ説明

試したこと [3]

最後に、該当のソースコードから非同期処理を削除 ( asyncawaitを削除 ) しデプロイしましたところ、上記のエラーメッセージはなくなりました。

しかし、fetch() の方もブラウザアクセスの方も、いずれも {} のみを得てしまいました。
ちなみにこの場合も Notion の方は上記と同様に無事ページは作成されました。

試したこと [まとめ]

・非同期処理があると上記 CORS のエラーメッセージが出る。
・非同期処理をやめるとエラーメッセージはなくなるが、非同期でないためにNotionAPIからの返り値の確認という (3) ができない。
・いずれにせよ Notion に無事ページは作成される。

こんな現状です。

補足情報(FW/ツールのバージョンなど)

バージョンなど特筆すべき補足は特にございませんが、質問内容に不足情報などあればご指摘頂ければ幸いです。

直接的な正解に限らず、上記の他に試すべきこと等ございましたら、ぜひご回答宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2023/08/31 01:14

補足願います。 1. UrlFetchApp.fetch を Promise でラップしている狙いを教えてください。 2. 不勉強で申し訳ないのですが、doGet 関数に async をつけている例を知りませんでした。通常の場合、doGet 関数はレスポンスを返すのだと思いますが、async がついた場合、doGet 関数は何を返すのか教えてください。 3. 試したこと [3] の際には、ラップしている Promise を外していますか?
kiringiraffe

2023/08/31 01:48

コメントありがとうございます。 補足1. ネットで見つけたコードのコピペで、こう使うものだと思っておりました。狙いはございませんでした。 補足2. レスポンスを返すという点では同様の考えです。しかし、main() の実行を待つために await main() としなければならないと考えました。すると main() をラップしている doGet() は async doGet() としなければエラーになりました。なので doGet() を async doGet() としました。 補足3. いいえ、外しておりません。外して試しましたら・・・解決致しました! これは「async await で待つ必要はそもそもなかった」ということでしょうか?
Lhankor_Mhy

2023/08/31 03:12 編集

ご解決されて何よりです。 お手数ですが自己解決の処理をお願いします。 --- > これは「async await で待つ必要はそもそもなかった」ということでしょうか? UrlFetchApp.fetch は、fetch と異なり同期的に動作しますので、そういうことかと思います。 蛇足ですが、CORS違反のエラーが出ていたのは、試したこと [2]のエラー画面には Access-Control-Allow-Origin ヘッダが付されていないことが原因かと思います。 試したこと [2]のエラーは、doGet が非同期であったため Promise を返したので、サポートされている戻り値の型ではなくなったことによると思います。
kiringiraffe

2023/08/31 10:02

最後まで大変お詳しいご説明に改めて感謝申し上げます。 > 試したこと [2]のエラー画面には Access-Control-Allow-Origin ヘッダが付されていないことが原因かと思います。 恐れ入りますが、こちらはどういうことでしょうか? 試したこと[2] において、Access-Control-Allow-Origin ヘッダを付与できるような箇所はなさそうに思われます。 もし [1] や [3] のように fetch() をするならその際の headers に記述するのだろうと思いますが、[2] は fetch() でなくブラウザアクセスなので、headers を記述する箇所がなさそうということです。
Lhankor_Mhy

2023/08/31 10:25

わかりにくくてすみません。リクエストヘッダのことではなくて、レスポンスヘッダのお話です。 ご存知の通り、レスポンスヘッダに Access-Control-Allow-Origin ヘッダが適切に付されている場合、CORS違反を回避することができます。 逆に言うと、エラー画面のレスポンスにCORS違反が出ているので、Access-Control-Allow-Origin ヘッダが適切に付されていないのだろう、ということです。 https://script.google.com/macros/s/●●●/exec の正常なレスポンスは Access-Control-Allow-Origin ヘッダが Google によって付されているかと思います。 しかし、エラー画面には Access-Control-Allow-Origin ヘッダが Google によって付されておらず、そのためにCORS違反のエラーメッセージが出ているのだろう、ということを申し上げています。 --- 何が言いたいのかというと、GASのWebAPIを叩いてCORS違反のエラーメッセージが出た場合、CORSの設定などを見直す前に、まず「正常な終了をしていないのではないか」と疑った方がいいな、ということです。 こちらのページが参考になります。 https://www.bugbugnow.net/2019/06/gasweb.html
kiringiraffe

2023/08/31 13:26

なるほど。GAS成功時の応答ヘッダーには access-control-allow-origin: *が含まれるはずだしその場合は CORS エラーにはならないはずなので、今回エラーになったということは応答ヘッダーに原因があるのだろうということですね。度々丁寧なご返信を誠にありがとうございます。リンク先も大変充実した内容でした。
guest

回答1

0

自己解決

質問へのコメント欄 にて Lhankor_Mhy様 がご指摘くださった点を修正し解決できました。

というのは、試したこと [3]

非同期処理を削除 ( asyncawait を削除 )

しておりましたが、さらに Promise でのラップ部分も削除しなければならなかったようでした。

自己解決というわけではないのですが、質問を閉じるためにこの形式をとらせて頂きます。
ご覧頂いた皆様、そして詳細にご教示くださった Lhankor_Mhy様、どうもありがとうございました。

投稿2023/08/31 03:52

kiringiraffe

総合スコア4

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問