AppEngineの便利な使い方。REST APIを実装して、ブラウザ上からDatastoreやPubSubにデータを格納する。

Hiroki Tanaka
google-cloud-jp
Published in
13 min readMay 7, 2018

--

Disclaimer : The views, information, or opinions expressed herein are solely my own and do not necessarily represent the policies or the opinions of any entity in which I have been or am now affiliated, including my employer.

突然ですが、下の画面のようなアンケートをウェブサイト上で実施する事って経験あります?

ある!という方ようこそいらっしゃいました。この記事ではそんな方が対象です。ないよ!する予定もないよ!という方、そっとこの記事を閉じて、他の記事へどうぞ。

では、『ある!』の方。そのアンケート結果の集計作業は、すぐにでも可能になっているでしょうか?それともバッチ処理が走るのを待つ必要がありますか?

もっと早く集計ができれば、エンドユーザーからのレスポンスに基づいて配信するコンテンツをダイナミックに変更したり色々できるのに、、と思ったことは無いでしょうか。

ここではそんなお悩みを解決する方法をご紹介しますね。

その方法とは、、、

AppEngine(GAE)を使ってREST APIを実装し、JavascriptからそのAPIを利用するという方法です!! ※1

なんでGAEでAPI化??と思った方。メリットはこちら。

  1. バックエンド側との疎結合化。
    GAEのメリットというより、API化のメリットですが、REST API化すれば、Javascript側が考慮すればよいのは、REST APIが返す値のみです。仮にAPIがダウンしていても、HTTPレスポンスが返すエラーコードの範囲内で済みます。
  2. バックエンドのDatabaseの選択肢が増える
    これはGAEを使うことのメリットですが、DatastoreやPubSubなどGCPが提供するDataBase製品を選択肢として利用可能です
  3. セキュリティを確保するレイヤーの分離
    バックエンドの選択肢が増えるのはメリットですが、エンドユーザーのブラウザからデータベースへのI/Oが発生するセキュリティが不安です。エンドユーザーがアクセス可能な範囲をGAEのAPIに限定することで、必要なセキュリティはGAE側に分離させることができます。

※1 わざわざGAEでAPIを実装しなくてもFirebase/Firestore があるじゃないかと思った方。そのご意見は正しいです!ただしバックエンドの選択肢を増やし、オウンドメディア以外(例えば媒体サイト)でも実装可能かどうかを考えると、GAEをREST API化しJavaScirptで簡単に呼びだせるようにするメリットがあるといえます。

それでは早速GAEを使ってREST APIを構築していきましょう。

手順1

今回は、GAE Standard環境で 、Go言語のREST APIを実装しました。まずはGoで開発ができる環境の準備をして下さい。GCPのドキュメントに解説されている手順を実行して、Hello Worldアプリが作れたら、開発環境は問題ありません。

Quickstart for Go App Engine Standard Environment

https://cloud.google.com/appengine/docs/standard/go/quickstart

手順2

開発環境を作ることができたら、次に簡単なREST APIをGAEで構築します。今回は、Gorilla/muxのパッケージを使ってURLマッピングを行う簡単なAPIを実装します。

APIの仕様ですが、

https://sample.appspot.com/api/users/1234567

上記のURLに対するhttp Request を行うと、URLに含まれる”1234567" の値を取得して、http response として返すAPIです。

サンプルコード例

package sampleimport (
"github.com/gorilla/mux"
"net/http"
"fmt"
"encoding/json"
"google.golang.org/appengine"
)
func init() {
r := mux.NewRouter()
r.HandleFunc("/api/users/{id:[0-9]+}", UserHandler).Methods("GET")
r.Host("localhost")
http.Handle("/", r)
}
type Reply struct {
Message string `json:"message"`
}
type Employee struct {
Name string
}
func UserHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
vars := mux.Vars(r)
id := vars["id"]
ctx := appengine.NewContext(r)
e1 := Employee{
Name: id,
}
reply := Reply {
Message: "id(" + id + ")を取得しました",
}
json, _ := json.Marshal(reply)fmt.Fprint(w, string(json))}

実際に実装するにはCloud Aceさんの記事が参考になります。

https://www.apps-gcp.com/gae-go-rest-api-1/#JSON

APIが正常に動作するかを確認するには、開発マシンのターミナルから以下のコマンドを実行して、

curl -I https://sample.appspot.com/api/users/1234567

{“message”:”id(1234567)を取得しました”}

というメッセージが返ってくればREST APIが正しく動作しています。

sample.appspot.comの部分は実際には皆さんのAppEngineのサブドメインになります。

ここまでで、GAEを使ってREST APIを実装することまではできましたが、このままではURL内の変数をそのままHTTPレスポンスとして返しているだけですので、引き続き入力値をGCPのデータベース製品(DatastoreやCloudSQL)に格納するところを実装してみたいと思います。

注意点として、GAEでREST API化する際に、公開範囲やセキュリティ対策は十分に注意して下さい。※2

外部からDatastoreに直接値を格納できるので、悪意のあるユーザーが何度も値を送りつけてくる可能性はゼロではありません。

※2 IAPを使う方法や、RateLimitを設定する方法がありますが、この記事では詳細は扱っておりません。

手順3(Datastoreとの連携)

次にDatastoreに値を格納する方法です。

Go言語のチュートリアルページに詳しく載っていますが、

https://cloud.google.com/appengine/docs/standard/go/datastore/entities

Datastore上に保存するデータの構造体を宣言します。今回は手順2で作ったREST APIで受け取れるようにしたユーザーIDのみを宣言します。

https://sample.appspot.com/api/users/1234567の1234567を格納する構造体ですね。

type Employee struct {
UserID string
}

この構造体に、手順2で実装したURLから取得したUserIDを代入して、datastore.Put 関数に使用すれば保存されます。GAEとDatastoreの連携はこのコードの通り非常に簡単に実装できます。

key := datastore.NewIncompleteKey(ctx, “employee”, nil)
datastore.Put(ctx, key, &e1)

これで、GAE側のREST APIの設定は完了です。

先程のコードにDatastoreとのやり取りを追記したものがこちらです

package sampleimport (
"github.com/gorilla/mux"
"net/http"
"fmt"
"encoding/json"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
)
func init() {
r := mux.NewRouter()
r.HandleFunc("/api/users/{id:[0-9]+}", UserHandler).Methods("GET")
r.Host("localhost")
http.Handle("/", r)
}
type Reply struct {
Message string `json:"message"`
}
type Employee struct {
Name string
}
func UserHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
vars := mux.Vars(r)
id := vars["id"]
ctx := appengine.NewContext(r)
e1 := Employee{
Name: id,
}
// put
key := datastore.NewIncompleteKey(ctx, "employee", nil)
datastore.Put(ctx, key, &e1)
reply := Reply {
Message: "id(" + id + ")を取得しました",
}
json, _ := json.Marshal(reply)fmt.Fprint(w, string(json))}

手順3(PubSubとの連携)

GAEからPubSubに投げる方法もあります。サンプルソースコード付きのヘルプ記事があるのでこちらを参照下さい。(Flexible環境向けです。)

https://cloud.google.com/appengine/docs/flexible/go/writing-and-responding-to-pub-sub-messages

手順4

これでAPIの作成は完了ですが、このままだとブラウザ上のデータをDatastoreに送るという部分のイメージがわかりにくいです。

そこでこのAPIを呼び出すドライバとなる、html のコードも作ってみましょう。

実装例がこちらです。これをブラウザで読み込むと、簡単な input form が表示されます。ここに任意の文字列を入力して、送信ボタンを押下しDatastoreに値が挿入されたことを確認できたら成功です。

<html>
<head>
<script language=”JavaScript”>
function createXMLHttpRequest() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest()
} else if (window.ActiveXObject) {
try {
return new ActiveXObject(“Msxml2.XMLHTTP”)
} catch (e) {
try {
new ActiveXObject(“Microsoft.XMLHTTP”)
} catch (e2) {
return null
}
}
} else {
return null
}
}
function send_get() {
var url = “https://sample.appspot.com/api/users/” + document.forms.form1.elements.text1.value;
var request = createXMLHttpRequest();
request.open(“GET”, url, true);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
//受信完了時の処理
var result = document.getElementById(“result_get”);
var text = document.createTextNode(decodeURI(request.responseText));
result.appendChild(text);
}
}
request.send(“”);
}
//送信ボタンにonclickイベントを追加
window.onload = function() {
document.getElementById(“getbutton”).onclick = send_get;
}
</script>
<title>GAE API demo</title>
</head>
<body body bgcolor=”#87cefa”>
GAE API Demo
<form name=”form1">
<input type=”text” name=”text1" value=””>
<input type=”button” name=”Submit” value=”送信” id=”getbutton” />
</form>
<div id=”result_get”></div>
</body>
</html>

応用例

ではこのAPIどんな実際の使い方があるのでしょうか?例えば広告配信のバナー上で使う方法があります。

Googleの広告配信ソリューションであるDoubleClickを使えば、クリエイティブ上で簡単なアンケートを実行したり、ユーザーのインタラクションによってクリエティブに表示する中身が変わる広告バナーを配信できます。

こうして広告媒体上で実行されたアンケートデータを、GCP上に格納するには、DoubleClickのログから取得する方法がありますが、ログファイルが生成されるまでに数時間のタイムラグが発生します。

このGAEを使ったWebAPIを利用すれば、ほぼリアルタイムで広告バナー上で入力されたアンケートデータと、自社のデータを連携させてデータ分析をすることができます。

※実際にそのようなクリエイティブを配信するには、媒体側のポリシーに適している必要があります。

GAEを使ったAPIの使用例いかがだったでしょうか?様々な応用ができると思いますのでぜひご検討ください。

--

--

Hiroki Tanaka
google-cloud-jp

Customer Engineer at @GoogleCloud. Disclaimer. Opinions are my own, NOT the views of my employer. All posts here are my personal opinion.