Google Apps ScriptでWebアプリケーションを作ると管理が楽
GASことGoogle Apps Script、便利ですよねー。サーバーレス最高です。
簡単にWebアプリケーションが作れてるし、Googleサービスも認証とかの知識がなくても使える。
もちろん向いてない用途はありますが、それは別のやり方を考えればよいわけで、私はとても便利に使っています。
GASでWebアプリケーションを作るパターンは決まっているのですが、毎回思い出したり調べたりするのも面倒なのでここにまとめておくことにしました。
動作はHTMLファイルを返す場合と、JSONを返す場合の2パターン
私がGASでWebアプリケーションを作る時は2つのパターンがあります。
ひとつはローカルで動いているHTML+Javascript+CSSのアプリをネット上に上げるパターンです。GASをWebサーバー的に使います。
GASに上げておけば、同じアプリを色んな端末、好きな場所から使えるようになります。特にモバイルで使えるのは大きいですね。
もうひとつは、クライアントJavascriptのリクエストに応えてJSONを返すパターンです。
たいていはGoogleスプレッドシートやGoogleカレンダーの読み書きです。
別にデータのやりとりはJSONでなくても良いのですが、JSONで問題無く動くコードができたのでそれを使っています。
GASの機能としてはプレーンテキストとかHTML文字列を生成して返すこともできるけど、実際問題、私は使わないです。
また、リダイレクトを伴うGET、POSTアクセスも普段使わないのでここには書きません。
上記の二つを組み合わせる時もあります。GASでHTMLファイルのホスティングをしながら、Googleスプレッドシートデータへの読み書きも行うような場合です。
とりあえずスプレッドシートのバインドスクリプトにする
GASはスクリプト単体でも作れますが、とりあえずスプレッドシートに付属したコンテナバインドスクリプトにしておくと便利です。
シートをログ代わりに使ったり、設定値をスプレッドシートに書き込むようにできます。
特に設定値をスプレッドシートに書いておくと、ちょっとした設定変更のために再デプロイする手間が省けてとても良いです。
パターン1:HTML,Javascript,CSSをホスティング
ホスティングという言葉が正しいのか分かりませんが、GASをWebサーバー的に使うパターンです。
ローカルで作ったWebアプリをネット上に上げるときなんかに使います。
やっていることは単純で、GETリクエストに対してindex.htmlの内容を返すだけです。サーバーサイドのデータ処理はありません。
index.html、script.html、style.htmlファイルを用意する
GASのエディタ(新エディタ)でHTMLファイルを追加します。
名前は何でも良いですが拡張子はhtmlです。普段はscript.jsと書いていてもここでは、script.htmlとします。名前だけ入れれば自動的に拡張子".html"が追加されます。
私は普段よく使う名前(index.html、script.html、style.html)を使っています。
index.html内にHtmlService.createHtmlOutputFromFileでリンクを張る
GASでホスティングする場合には、JavascriptファイルとCSSファイルを読み込むためのリンクの張り方が違います。
<script src=や<link rel= ではなく、以下のようにGAS独自のメソッド HtmlService.createHtmlOutputFromFile を使ってHTML内に読み込みます。
場所は通常のHTMLと同じ、cssは<head>内、Javascriptは<body>タグの最後です。
<?!= ?>
はGASのHTML内でJavascriptコードを実行するためのタグです。
この中に書かれたコードではGAS独自の関数やメソッドも使えます。
<head>
<?!= HtmlService.createHtmlOutputFromFile('style').getContent(); ?>
</head>
<body>
<?!= HtmlService.createHtmlOutputFromFile('script').getContent(); ?>
//<script src="./script.js"></script>
</body>
コピペ用ひな形
<link rel=>と<script src=>をコメントとして入れています。
ローカルで開発する時には、このコードを生かして、GAS用のコードをコメントアウトします。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= HtmlService.createHtmlOutputFromFile('style').getContent(); ?>
//<link rel="stylesheet" href="style.css">
</head>
<body>
<p>HTML OUTPUT</p>
<?!= HtmlService.createHtmlOutputFromFile('script').getContent(); ?>
//<script src="./script.js"></script>
</body>
</html>
【追記】以前はsytle.htmlやscript.htmlに素のCSSやスクリプトを書けた気がするのですが、最近やったら<style></style>や<script></script>タグで囲まないと単なる文字列として扱われてしまいました。
style.htmlとscript.htmlの内容を以下のようにしたらうまくいきました。
style.htmlの中身
<style>
......cssを書く
</style>
script.htmlの中身
<script>
......javascriptを書く
</script>
GASはdoGet()でindex.htmlを返す
GAS側はdoGet()でHtmlService.createTemplateFromFileを使ってindex.htmlを返します。
JSやCSS読み込み用に設置した<?!= ?>
コードを実行するために、.evaluateメソッドが必要です。
function doGet() {
return HtmlService.createTemplateFromFile("index.html").evaluate();
}
あとはWebアプリケーションとしてデプロイすれば、ブラウザからアクセスして使うことができます。
基本的にローカルで動いたHTML+Javascript+CSSのアプリは、この方法でGAS上に上げることができます。
パターン2-1:Javascriptからの要求にJSONを返す(GETリクエスト)
次はクライアントから処理のリクエストがあったときに、処理結果をJSONで返すパターンです。API的な使い方ですね。
ajaxでGET/POSTリクエストを送ることでリダイレクトせずにサーバーからのデータを受け取ります。
クライアント:GETリクエストでJSONを受け取るコード
$ajaxを使うためにHTML側でjQueryを読み込んでおきます。
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
Javascriptコードは以下のようになります。GETメソッドにパラメーターを付けてサーバーに情報を渡します。
ここでは、actionとdataという二つのパラメーターを想定しています。
actionでサーバーの動作を指定して、dataでそれに必要な値を渡します。もらろん、作るアプリケーションによって変えます。
ENDPOINTにはGASでデプロイしたWebアプリケーションのURLを設定します。
const ENDPOINT="https://~~ web app url /";
const action = "reply";
const data = "12345";
$.ajax({
type: "GET",
url: END_POINT,
data: { action:action,data:data }
}).done((result) => { // 成功した時の処理
dataObj = JSON.parse(result);
console.log(dataObj);
}
}).fail((error) => { // 失敗した時の処理
alert('Error:' + JSON.stringify(error));
}).always((res) => {// 常にやる処理
// do something
});
コピペ用のひな形
ひとつのHTMLファイルに収めたひな形です。
<!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>Document</title>
</head>
<body>
<p>GAS GET Access Template</p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
window.onload = function () {
const END_POINT = "https://*********************";
const action = "reply";
const data = "12345";
$.ajax({
type: "GET",
url: END_POINT,
data: { action: action, data:data }
}).done((result) => { // 成功した時の処理
dataObj = JSON.parse(result);
console.log(dataObj);
}).fail((error) => { // 失敗した時の処理
alert('Error:' + JSON.stringify(error));
}).always((res) => {// 常にやる処理
// do something
});
}
</script>
</body>
</html>
サーバー側:JSON.stringifyしたデータをContentService.createTextOutput()で返す
GASのサーバー側では、doGet()関数内でパラメーターを取り出して、switchで分岐させています。
function doGet(e) {
const reqParam = e.parameter;//パラメーターを取得
switch (reqParam.action) {//actionパラメーターの内容によって処理を分岐
case "reply": //パラメーターをそのまま返すアクション
{
const data = JSON.parse(reqParam.data);
return ContentService.createTextOutput(JSON.stringify(data));//単にdataパラメーターを返す
}
break;
//以下、case文を必要なだけ書く
default:
{
const result = 'did nothing';
return ContentService.createTextOutput(JSON.stringify(result));
}
break;
}
アクセス権を全員以外にするときの注意
Webアプリケーションとしてデプロイする時のアクセス権設定を"全員"以外にするときには注意が必要です。
適切なGoogleアカウントでログインしたブラウザからアクセスしないと、以下のような結果が返ってきて、ブラウザ側でCORSエラーになります。
〜 has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
パターン2-2:Javascriptからの要求にJSONを返す(POSTリクエスト)
Postリクエストを使いたい時には、クライアントからajaxでPOSTリクエストを送り、サーバー側はdoPost()で受け取ります。
クライアント:POSTリクエストでJSONを受け取るコード
const ENDPOINT = "https://******";
const data = "123456";
$.ajax({
type: "POST",
url: END_POINT,
dataType: "json",
data: { action: 'reply', data: JSON.stringify(data) }
})
.then(
(result) => { // 成功した時の処理
console.log(JSON.stringify(result));
},
(error) => { // 失敗した時の処理
alert('Error:' + JSON.stringify(error));
}
);
サーバー側:JSON.stringifyしたデータをContentService.createTextOutput()で返す
基本的にはdoGet()をdoPost()に変えただけです。
function doPost(e) {
const reqParam = e.parameter;//パラメーターを取得
switch (reqParam.action) {//actionパラメーターの内容によって処理を分岐
case "reply": //パラメーターをそのまま返すアクション
{
const data = JSON.parse(reqParam.data);
return ContentService.createTextOutput(JSON.stringify(data));//単にdataパラメーターを返す
}
break;
//以下、case文を必要なだけ書く
default:
{
const result = 'did nothing';
return ContentService.createTextOutput(JSON.stringify(result));
}
break;
}
パターン3:ホスティングとJSON返しの両方を行う
index.htmlを返すホスティングとJSONを返すAPIの両方を使うパターンです。
この場合は、パラメーターなしでGETリクエストが来たらindex.htmlを返し、パラメーターがあればそれに応じた処理を返します。
パラメーターの有無は以下の方法で判定しています。
if (Object.keys(reqParam).length < 1)
function doGet(e) {
const reqParam = e.parameter;//パラメーターを取得
//パラメーターが付いていなければindex.htmlを返す
if (Object.keys(reqParam).length < 1) {
myHTML = HtmlService.createTemplateFromFile("index.html");
return myHTML.evaluate();
}
//パラメーターがあれば、内容に合わせて処理を分岐させる
//ここでは、"action"というパラメーターでSwitchさせている
switch (reqParam.action) {//actionパラメーターの内容によって処理を分岐
case "reply": //パラメーターをそのまま返すアクション
{
const data = reqParam.data;
return ContentService.createTextOutput(JSON.stringify(data));//単にdataパラメーターを返す
}
break;
//以下、case文を必要なだけ書く
default:
{
const result = 'did nothing';
return ContentService.createTextOutput(JSON.stringify(result));
}
break;
}
最後までローカルで開発したいのでgoogle.script.runは使わずにGET、POSTで処理する
このパターンでは、クライアントのJavascriptからGASの関数を呼び出す際にもGET/POSTリクエストを使います。
GASでホスティングしたHTMLやJavascript内では google.script.run
という命令を使ってGASの関数を直接呼び出せます。
ただ、この機能はGASでホスティングしたときしか使えないので、開発がちょっと面倒です。
GASのエディタもかなり良くなりましたが、HTMLファイルの中にJavascriptを書く時などは、補完や自動成形があまり使えなくて不便です。
すべてGET/POST経由で処理することで、完成するまでローカルのIDEで開発できます。
ローカル+GASで完成したものを、ちょっと修正してGASでホスティングというのが、個人的には一番効率が良いかなと思います。
あんまり長くても不便なので、今回はこのへんで終わりにします。
スプレッドシートデータの読み書きなどもテンプレ化しておきたいと思います。