LoginSignup
38
38

More than 5 years have passed since last update.

Google Drive Host PageとGoogle Apps Scriptで簡単な掲示板作った話

Last updated at Posted at 2013-02-15

Google Driveで静的なWebサイトが作れるようになりましたね。
http://www.itmedia.co.jp/news/articles/1302/06/news139.html

今回はせっかく出来たGoogle Driveの静的なページ(以下Drive Host Pageと書きます。正式名称じゃないっす。)とGoogle Apps ScriptのContentServiceを使って簡単な掲示板を作ってみたよって話を書きます。

ゴール

以下が作ったサイトです。
https://googledrive.com/host/0BwzWIlBMCiR9SlZkbkFqZ21YeFk/

Drive Host Pageの作り方

まずDrive Host Pageの作り方

  1. Google Drive内にどこでもいいのでフォルダを作ります。
    今回はhome/掲示板/webappというフォルダを作りました。

  2. 1.で作った公開したいファイルを置くフォルダの共有設定をPublic on the Webにします。
    誰でも閲覧可の状態 今回はwebappをこの設定にしています。

  3. 1.のフォルダにサイトとして公開したいhtmlファイルを置きます。
    あまりいないかもですがもしこの時、GASなどのツールを使ってアップロードした場合、
    Content-Typeをtext/htmlで指定してないとうまくいかない場合があります。

  4. 置いたhtmlファイルをdrive上で開きます。そうするとPreviewというボタンが表示されます。それを押下します。

  5. 多分https://googledrive.com/host/{公開フォルダのID}/{ファイル名}のURLに遷移して、webページが表示されます。

こんな感じで、公開したフォルダの配下にwebサイトとして公開したいファイルを置いていきます。

なお、js用のフォルダや、img用のフォルダを切った場合はhttps://googledrive.com/host/{公開フォルダのID}/{作ったフォルダ名}/{ファイル名}の形でアクセスできます。

GASのContentServiceを使ってwebサービス(Web API)を作る

Google Apps ScriptのContentServiceはtext、json、xml(atomやrssを含む)等のhtml以外のコンテンツをwebサービスとして返却するためのサービスです。

例えば単純なJSONを返したい場合は以下のように書きます。

function doGet(e) {
  var jsonObject = {hoge : 'fuga'};
  return ContentService.createTextOutput(JSON.stringify(jsonObject)).setMimeType(ContentService.MimeType.JSON);
}

ただしこのサービスが公開されるURLはhttps://script.google.com/macros/s/{id}/execとなるため、
上ほど作成したDrive Host Pageとは異なるドメインになってしまいます。
なのでJavascriptからは直接アクセスができません。
※GASのContentServiceで返却されるコンテンツにはAccess-Control-Allow-OriginヘッダもついてないのでCross Domain XHRも無理っす

そこでGAS側でJSONP形式でも返却するようにしておきます。

function doGet(e) {
  var jsonObject = {hoge : 'fuga'};
  if(e.parameter.callback) {//callbackはクライアントアプリケーションから指定されるcallback関数名
    return ContentService.createTextOutput(e.parameter.callback + '(' + JSON.stringify(jsonObject) + ')').setMimeType(ContentService.MimeType.JSON);
  } else {
    return ContentService.createTextOutput(JSON.stringify(jsonObject)).setMimeType(ContentService.MimeType.JSON);
  }
}

またGASではGETとPOSTのみサポートされており、かつURIに寄るRESTっぽいAPIも作れません。
なので操作(データを取得するとかデータを追加する)は全てパラメータとして渡します。

function doGet(e) {

  if(!e.parameter.action) { //actionはapiの操作
    return createContent(e.parameter.callback , {error :'action is required '});
  }

  switch(e.parameter.action) { //switchで書いてますが、規模によってはstrategyパターンとかにしたほうがいいかもですね
    case 'get':
      return createContent(e.parameter.callback , doSomething1(e));
    case 'put':
      return createContent(e.parameter.callback , doSomething2(e));
    default : 
      return createContent(e.parameter.callback , {error : "unsupported operation"});
   } 
}

function createContent(callback , returnObject) {
  if(callback ) {//callbackはクライアントアプリケーションから指定されるcallback関数名
    return ContentService.createTextOutput(callback + '(' + JSON.stringify(returnObject) + ')').setMimeType(ContentService.MimeType.JSON);
  } else {
    return ContentService.createTextOutput(JSON.stringify(returnObject)).setMimeType(ContentService.MimeType.JSON);
  }


}

後はクライアント(Drive Host Pageのjsとか)からJSONPでアクセスすればデータを取得出来ます。

認証とか

GASはGoogle Appsドメインを除き、認証(ユーザを判別する方法)は1種類しか無いです。

GASをWeb Appとして公開するときに指定する「Execute the app as:」を「User accessing the web」にし、
ユーザがAPIに初回アクセスするときに表示されるダイアログを利用してインストール(インストールというよりOAuth認可に近いと思いますが)して貰う方法です。

もし「Execute the app as:」を「me」にしている場合、GASのSession.getActiveUser()というサービスでユーザは返却されますが、その後取得したUserオブジェクトのgetEmailメソッドなどは空を返却します。

※あとでキャプチャ貼る

残念ながらこの方法はContentServiceを利用した場合も同じで、Web APIとして、jsonが還ってくることを期待してjavascriptからアクセスしても、
還ってくるのは上のダイアログ(つまりhtml)になります。

そこで、今回作ったアプリではDrive Host Pageを開いた際にjavascriptで一度jsonpとしてアクセスし、
返却されたテキストをjsonと解釈させ、エラーが発生した場合は認証(GASのインストール)が済んでないと考え、
認証してもらうためのリンクを表示しています。

//クライアントサイドのJS angularjsを利用しています。
$http({
  method: 'JSONP',
  url: 'https://script.google.com/macros/s/AKfycbylYV5MrVg3d-Yi3IssTzo4_xIGsfGs0fgLY1fMcF5WUc02UKM/exec?callback=JSON_CALLBACK&action=messages'
}).success(function(data, status) {

  //successの場合はjson文字列の評価が成功 == 認証済み
  $scope.messages = $scope.messages.concat(data);
}).error(function(data,status,headers,config){

  //errorの場合はjson文字列の評価が失敗 == 多分認証できてない => 認証してもらうためにリンクを表示
  $scope.showAuthentification = true;
});

また認証は 元のアクセスしたGASのURL → Googleの認証URL → 元のアクセスしたGASのURL と遷移するので、
Web APIを表示しているGAS側で元のDrive Host Pageに戻るためのリンクを表示させます。

function doGet(e) {
  if(e.parameter.auth) {  //認証時はauthというパラメータを付けておく
    return HtmlService.createHtmlOutputFromFile("index.html"); //認証後に元のDrive Host Pageに戻ってもらうためのリンクを表示する画面を返却する。
  }

  //web apiの処理が続く
  ...
}

なお、Google AppsのGASの場合は「Execute the app as:」を「me」にしていても、GASのSession.getActiveUser().getEmail()はアクセスした
ユーザのアドレスが返却されるため、上記のような操作は不要になります。

データの保存

ここまで来ればデータの保存はどこにやっても良いです。
今回のアプリでは返却するJSONをラクに作るためにScriptDbを利用していますが、
サービスの種類によってSpreadsheetにしたり、ScriptPropertiesにしたり、Fusion Tablesにしたりと適切なものにして下さい。
ただし、「Execute the app as:」を「User accessing the web」にしているため、
SpreadsheetやFusion Tablesの場合はユーザ自身が、そのファイルへのアクセス権限を保つ必要があります。

DBを公開したくない場合はScriptDbや、ScriptPropertiesなどScript自体にデータが紐づくサービスを利用したほうが良いです。

P.S.
Google Drive Host Pageが2/6に追加されたって書いてあるんですが仕組み的には昨年ぐらいに追加されているんですよね
なんでGoogleも改めて発表したのか若干気になったりします。

38
38
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
38