curlではPOSTできたけどchromeではPOSTできなかった
以前、ブラウザ上のテキストをGoogleスプレッドシートに追記でメモるスクリプトを書きました。
-
ブラウザ上のテキストをGoogleスプレッドシートに追記でメモるAutomatorクイックアクション & Google Apps Script
細切れの情報を収集するときに便利なように、ブラウザ上のテキストをGoogleスプレッドシートに追記でメモるAutomatorクイックアクションを作りました
続きを見る
最近、ブラウザ上で入力したテキストをGoogle Documentに保存したくなり、試しにJavacriptで上記のWebアプリにPOSTしてみたところエラーになってしまいました。
ブラウザ(Chrome)のコンソールには「CORB = Cross-Origin Read Blocking」というエラー表示が出ていました。詳しいことはよく分かりませんがセキュリティ対策でこうなってるみたいです。
これを回避する方法をいろいろ調べて、あれこれ試してどうにかたどり着いたのが、今回のスクリプトです。
Google Apps ScriptへcurlでPOSTする記事は一杯あるのですが、ブラウザからPOSTする記事は少なくて苦労しました。
ホントは、あれはダメだったけど、こうしたら大丈夫だったという情報を残したいのですが、トライアンドエラーをやってるうちに、いつの間にかCORBエラーは出なくなり、代わりに別なエラーが出たりして、最後に動いたコードだけ残ったという素人あるあるなパターンです。
苦労したわりに結果のコードはシンプルです。
基本的にはブラウザに戻す値をJSONにするといいみたいです。なので、JSONでPOSTしてJSONを戻すという処理になっています。
ブラウザ側のhtml+Javascriptコード
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Post to Google Document</title> </head> <body> <textarea id="textArea" rows="4" cols="40">ここにPOSTしたいデータを入力します</textarea> <br> <button id="postButton">POST</button> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> <script> document.getElementById("postButton").addEventListener('click', postToGAS, false); function postToGAS() { const endpoint = "https://------------Google Web AppのURL--------------"; const textContent = $("#textArea").val(); $.ajax({ type: "POST", url: endpoint, dataType: "json", data: { memo: textContent } }) .then( result => console.log(JSON.stringify(result)), error => alert('Error:' + JSON.stringify(error)) ); } </script> </body> </html>
ブラウザ側は、textarea内のテキストデータを取り出して、ajaxのPOSTメソッドでアクセスします。
endpointにはGoogle Apps ScriptのWebアプリのURLをセットします。
datatypeはJSONにしてあり、{memo:テキストデータ} という形式で送信します。
formでPOSTせずにajaxを使うのは、投稿後に画面遷移(リダイレクト)させたくないからです。
今回は単純なテキストデータだけを送りますが、JSONなので必要ならもっと複雑なデータを書き込めます。
Google Documentのスクリプトエディタに設定するコード
var body = DocumentApp.getActiveDocument().getBody(); function appendTextToDoc(data) { var text = body.editAsText(); var now = Utilities.formatDate(new Date(), 'JST', 'yyyy/MM/dd HH:mm:ss'); text.appendText('\n\n------------------- stored at ' + now + ' ------------------------\n\n'); text.appendText(data); } function doPost(e) { const dataObj = e.parameter; appendTextToDoc(dataObj.memo); return ContentService.createTextOutput('{"done":"done"}'); //JSONを返すとエラーにならない? }
スクリプトは単体ではなく、Google Document内で[ツール][スクリプトエディタ]を使って作成します。
ブラウザからのアクセスはdoPost関数で処理します。
最初、うまくデータが取り出せずに、POSTだから e.postData.contents とかで処理するのかと思ったらやっぱり違って、GETと同じように e.parameter オッケーでした。
Google Apps ScriiptのWeb Appsドキュメント
ただし、 e.parameter.memo ではうまく投稿データを取り出せず、いったんdataObjというオブジェクトに入れてから dataObj.memo で取り出しています。よく分かりませんが動いたのでOK。
JSONを返す方法ですが、ContentService.createTextOutput()に、JSONを渡すとどうやら自動的にJSONになるみたいです。
GAS
return ContentService.createTextOutput('{"done":"done"}');
明示的にJSONを返したいときには、
GAS
return ContentService.createTextOutput('{"OK":"SUCCESS"}').setMimeType(ContentService.MimeType.JAVASCRIPT);
という書き方をするみたいです。
引数に直接JSONを書かずに変数で渡したいときはJSON.stringify します。
appendTextToDoc関数は、受け取ったテキストデータに日付入りの区切り線をつけてドキュメントに追記します。
ローカルWebアプリのデータ保存に使いたい
私はローカルWebアプリを作るのが好きなのですが、困るのがデータの保存です。
ブラウザはローカルファイルへのアクセスが厳しく制限されているので、データを保存する方法があまりありません。localstorageやcookieは、成果物の保存・利用という目的には使いにくい。
なので、例えばメモアプリを作ったとするとデータを保存するためにはコピペで他のエディタに貼り付けるみたいな事になります。(追記:そういえばダウンロード処理を使う方法がありました)
しかし今回のようにGoogle DocumentやGoogle SpreadSheetにPOSTできれば、色んなデータを保存して再利用できるようになります。
全部をGoogle Apps Script上で作るという手もあるのですが、いちいちデプロイしないと確認できないので開発が面倒くさいのです。
今回の方法でローカルWebアプリからGET,POSTでGoogle Document/SpreadSheetのデータが読み書きできれば、結構便利に使えそうです。
↓やってみました。
-
GoogleスプレッドシートをWebアプリのデータベース代わりに使う
GoogleスプレッドシートをローカルWebアプリのデータ保存先として使えるようにしました
続きを見る