Google AppScriptでHTTP POST(doPost)を受け付ける最小コード (2022年夏)

Google AppScriptでHTTP POSTを受け付ける方法は、ググればかなり出てきます。

2021年頃の仕様変更(参考URL[^1] [^2])によって `console.log` や `Logger.log` の出力はGoogle Cloudに紐づける必要があり、ちょっとした開発は面倒です。

[^1]: https://developers.google.com/apps-script/guides/logging
[^2]: https://ryjkmr.com/google-apps-script-console-log/

ここでは、ともかくHTTP POSTのBodyをSpreadsheetに出力するだけの最小コードを用意しました。これにより `e` の出力をとりあえず確認できるようになります。
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/b2fedcab-811f-5a52-653f-5a4182c0b243.gif)

:::note info
動画内の当該URLは(もちろん)無効化されてます。
:::

## 論よりコード

```javascript:コード.gs
function doPost(e) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];

  sheet.getRange("1:1").insertCells(SpreadsheetApp.Dimension.ROWS);
  sheet.getRange(1, 1).setValue((new Date).toLocaleString('ja-JP'));
  sheet.getRange(1, 2).setValue(e);
  sheet.getRange(1, 3).setValue(e.postData.contents);

  const output = ContentService.createTextOutput(JSON.stringify({result:"Ok"}));
  output.setMimeType(ContentService.MimeType.JSON);
  return output;
}
```
上記コードに対してHTTP POSTすると、スプレッドシート内の一番左(=先頭)シートに対して、最新が1行目に来るように記録されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/cddd4006-0516-77a4-336e-0ac6b23b290d.png)

### 使い方

1. 新規にGoogle Spreadsheetを作成
1. Spreadsheetの [拡張機能] > [App Script]
1. `myFunction` とコードを入れ替える
1. [デプロイ] > [新しいデプロイ]
    - [種類の選択] > ウェブアプリ
    - アクセスできるユーザー = 全員
    - [デプロイ] >> [アクセスを承認] >> [許可]

これで URL が払い出されます。
あとは例えば

```bash
curl -v -L -H "Content-Type: application/json" -d '{"b":200}' https://script.google.com/macros/s/APP_ID.../exec
```

のようにHTTP POSTすればOKです。
スクリプトを編集したらデプロイをし直してURLを再発行し、新たなURLにアクセスするようにしてください。

:::note warn
認証/認可の仕組みはありません。よって、URLが漏洩すると誰でもPOSTできることになるため、URLの取り扱いは厳重に行ってください。
URLの無効化は、当該Spreadsheetをゴミ箱=>"完全に削除"で行えます。Sheetの中身やスクリプトのバックアップをお忘れなく。
:::

### 実際の活用には...?

[Google Apps ScriptのdoPostでJSONなパラメータのPOSTリクエストを受ける](https://qiita.com/shirakiya/items/db22de49f00710478cfc#json%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%97%E3%81%91parse%E3%81%99%E3%82%8B) にも書かれているように `JSON.parse(e.postData.contents)` とすれば、Objectとして取り扱えます。
あとは `sheet.getRange(ROW, COL).setValue(VAL);` で記録していけるでしょう。

ちなみに `contents` と `getDataAsString()` は等価のようです。
`getDataAsString()`の引数には解釈するcharsetが指定できるので、contentsを使ってエンコーディングが変だった場合に有効かもです。


## `e` の中身について

`doPost(e)` もしくは `doGet(e)` で渡された `e` の中身は https://developers.google.com/apps-script/guides/web#request_parameters で解説されています。

ちなみに `e.postData` は FileUpload 型となっているのですが、肝心の定義が見当たりません。
[こちらの記事](https://qiita.com/shirakiya/items/db22de49f00710478cfc#json%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%97%E3%81%91parse%E3%81%99%E3%82%8B)によると[Blob](https://developers.google.com/apps-script/reference/base/blob)を継承しているとのこと。(だから[`getDataAsString()`](https://developers.google.com/apps-script/reference/base/blob#getDataAsString())がある)

メソッド/プロパティ一覧を `Object.keys(e.postData).toString()` で確認してみましたが `contents` `length` `name` `type` 以外で Blob と異なる点は見当たりませんでした。
Blogとして扱うのが良さそうです。

```javascript
Object.keys(e.postData).toString();
//=> toString,getName,getBytes,setName,getDataAsString,setDataFromString,isGoogleType,getAllBlobs,copyBlob,setContentTypeFromExtension,getContentType,setContentType,setBytes,getAs,contents,length,name,type
```

## あとがき

年次イベント、無事終わってよかったよ。

EoT