【Vue.js】 Google SpreadSheet をデータベースの代わりに使う。

# はじめに
JS、Vue.js ともに初学者です。
間違いもあるかと思いますので、指摘していただけたら幸いです。

# 環境

今回グーグルアカウントはG Suiteを利用しています。
なのでGAS,Spreadsheetともに公開範囲は社内のみとなっていますが、一般のアカウントでもOKです。

また、Vue CLI3を使ってプロジェクトを作成しています。

# 方法
1. SpreadsheetをJSON形式に変換し、Web APIとして公開する
2. VueでそのAPIを叩く
3. おしまい

# 問題
素直にJSONを取得しようとすると、CORSエラーが発生します。

## CORS(Cross-Origin Resource Sharing)とは？
ブラウザがオリジン(HTMLを読み込んだサーバのこと)以外のサーバからデータを取得する仕組みです。
デフォルトでは、自分自身以外のドメインからのデータ取得を拒否する設定になっているようです。

→簡単に言うと、認証されてないドメインからデータ取得させないようにする仕様です。

サーバから送信されるレスポンスヘッダに
`access-control-allow-origin:＊`
が付与されている場合、すべてのドメインにデータ取得を許可できます。

GASもCORSに対応しており、
レスポンスヘッダにも、`access-control-allow-origin:＊`が付与されます。
なのに何故CORSエラーが発生するのでしょうか？
調べたところ、うまく行かないパターンがあるようです。


><font color=red>**うまく行かないパターン**</font>
・JSONテキストをまるごと POST するような場合は エラーとなる
　→なぜなら、JSONテキストの content-typeが application/json となるため。
　→なぜ application/json だとエラーとなるかというと、 application/jsonの場合は **preflightリクエスト**が発生するため
　→なぜなら、Google Apps Script は <font color=red>OPTION メソッド</font>をサポートしていないため。

▲引用 - [今から10分ではじめる Google Apps Script(GAS) で Web API公開 - Qiita](https://qiita.com/riversun/items/c924cfe70e16ee3fe3ba#web-api%E3%82%92jsonp%E5%AF%BE%E5%BF%9C%E5%8C%96%E3%81%99%E3%82%8B)

正直理解しきれていないですが、どうやらJSONをそのままPOSTする場合はダメみたいです。
サーバ側の処理をいじることはできないので、クライアント側で対処します。


クライアント側の対処方法はいくつかあります。

1. ブラウザのセキュリティをOFFにする。
2. JSONPを使う。


### ブラウザのセキュリティをOFFにする場合
参考 : [クロスドメイン制約を回避するChromeショートカットを作る - Qiita](https://qiita.com/shika-e/items/808ccdd047133315b95c)
Chromeの起動オプションでセキュリティを切ります。

```
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C://Chrome dev session
```

手軽にできるので開発中には良いのですが、他人に成果物を共有するとき、
「<font color=red>※ブラウザのセキュリティを切っています</font>」
なんて注意書きを入れなきゃいけません。

### JSONPを使う場合

素のaxiosではJSONPが取得できないようなので、vue-jsonpというプラグインを導入します。
JSONPとは、コールバック関数を埋め込み、関数の戻り値としてJSON文字列を受け取る形式です。

※JSONPは情報漏洩の可能性があります。機密情報を取り扱わないようにしましょう。


→今回はGASの公開範囲によって社内限定にデータが守られているので、JSONPを使って実装します。


# 実装

## 1.SpreadsheetをJSON形式に変換し、Web APIとして公開する

JSON形式というかJSONP形式ですが、、
以下のGASを作成します。
参考 : [Google SpreadSheet のデータを JSON 形式で取得する Web API をサクッと作る - Qiita](https://qiita.com/takatama/items/7aa1097aac453fff1d53)

```js
function getData(id, sheetName) {
  var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
  var rows = sheet.getDataRange().getValues();
  var keys = rows.splice(0, 1)[0];
  return rows.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}

function doGet() {
  var data = getData('スプレッドシートID', 'シート名');
  return ContentService.createTextOutput("callbackFunction" + '(' + JSON.stringify(data, null, 2) + ')')
  .setMimeType(ContentService.MimeType.JAVASCRIPT);
}
```

作成したGASを「公開>ウェブアプリケーションとして導入」と進み
アプリケーションにアクセス出来るユーザーを 全員(匿名ユーザーを含む) にして公開します。
末尾に「exec」のついたURLが表示されるので、そのURLを使ってGASにアクセスします。

## 2. VueでそのAPIを叩く

参考 : [覚え書き Vue.jsでjsonpを使ってみる - Qiita](https://qiita.com/_masakitm_/items/ada0d210e94365b45db4)
先にvue-jsonpプラグインを導入しておき、以下のようにAPIを叩きます。
この場合jsonDateという変数にJSON形式のデータが格納されます。

```js

data() {
  return {
    jsonData:'' // JSONを格納するための変数
  }
},
methods: {
  getData (apiUrl) {
    // this.$jsonp(url, dataObj, timeout) で使える。 Vueコンポーネント内だとthisで呼び出せる。
    this.$jsonp("APIのURL",{ callbackName: "callbackFunction" })
    .then(json => {
      // Success.
      this.jsonData = json
    }).catch(err => {
      // Failed.
    })
  }
}
```

これでSpreadsheetをデータベース代わりに使うことができるようになりました！
もっと良いやり方を知っている人がいらっしゃいましたら御教示頂けると幸いですm(_ _)m

# 参考一覧
[Google SpreadSheet のデータを JSON 形式で取得する Web API をサクッと作る - Qiita](https://qiita.com/takatama/items/7aa1097aac453fff1d53)
[覚え書き Vue.jsでjsonpを使ってみる - Qiita](https://qiita.com/_masakitm_/items/ada0d210e94365b45db4)
[今から10分ではじめる Google Apps Script(GAS) で Web API公開 - Qiita](https://qiita.com/riversun/items/c924cfe70e16ee3fe3ba#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%8B%E3%82%89%E3%81%93%E3%81%AEapi%E3%82%92%E3%81%9F%E3%81%9F%E3%81%8D%E3%81%9F%E3%81%84)
[JSONPで悩むある程度の人々へ](http://tsujimotter.info/2013/01/03/jsonp/)
[CORS(Cross-Origin Resource Sharing)によるクロスドメイン通信の傾向と対策 ｜ DevelopersIO](https://dev.classmethod.jp/cloud/cors-cross-origin-resource-sharing-cross-domain/)