Go製のフレームワークechoを使ってJSONを返すWebサーバーを作り、GoogleAppEngineで動かす

  • 126
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

UPDATE 2016/06/12
2.0がリリースされました。
v2.0.1でやってみたところこの記事内容は動くようですが、少しだけ変更点があるようです。
https://echo.labstack.com/guide/migrating

また、ローカルでバージョンアップするには以下のようにします。

# テストのフレームワークがないよと怒られるので事前に取得します。
go get github.com/stretchr/testify
# echoのアップデート
go get -u github.com/labstack/echo/...

GoでJSONを返すAPIサーバを作ってみたかったので調べました。

echoとは

Goで作られている軽量なフレームワークで、他のものに比べて現在も開発が活発です。最新版はv1.4ですが、v2.0.0betaが出ているので近々バーションアップされるようです。

Echo is a fast and unfancy HTTP server framework for Go (Golang). Up to 10x faster than the rest. https://labstack.com/echo
https://github.com/labstack/echo

Installation

チュートリアルに従ってやってみます。

# echoのインストール
go get github.com/labstack/echo/...
# プロジェクトのディレクトリを作ります
cd $GOPATH/src
mkdir echo-sample
cd echo-sample

app.goというファイルを作ります。

app.go
package main

import (
    "net/http"
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Run(standard.New(":1323"))
}

で、実行。

go run app.go
# 何も表示がなくわかりづらいですが、http://localhost:1323 に'Hello, world'と表示されます

GoogleAppEngineで動かす

ローカル環境で動かすのは簡単なのですが、GAEで動かすのにルーティングがうまくいかず悩んでいたら普通に解説がありました(--#)

https://labstack.com/echo/recipes/google-app-engine

これに沿って行きます。

JSONを返すAPIと静的なファイル配信

app.go
package main
// 起点となるファイルです
var e = createMux()
app-standalone.go
// +build !appengine,!appenginevm

package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "github.com/labstack/echo/middleware"
)

// ログの設定や静的ファイルの場所指定などをしています
func createMux() *echo.Echo {
    e := echo.New()

    e.Use(middleware.Recover())
    e.Use(middleware.Logger())
    e.Use(middleware.Gzip())

    e.Use(middleware.Static("public"))

    return e
}

func main() {
    e.Run(standard.New(":8080"))
}

publicを静的ファイルの場所としたので、HTMLでも作っておきます

public/index.html
<h1>HELLO, ECHO!</h1>

Userの情報をJSONで返します

users.go
package main

import (
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "github.com/rs/cors"
)

// UserのJSONの形を指定します。
type (
    user struct {
        ID   string `json:"id"`
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
)

var (
    users map[string]user
)

func init() {
    // 初期データを登録してみます
    users = map[string]user{
    "1": user{
        ID:   "1",
        Name: "ジョナサン・ジョースター",
        Age:  20,
    },
    "2": user{
        ID:   "2",
        Name: "ディオ・ブランドー",
        Age:  21,
        },
    }

    g := e.Group("/users")
    g.Use(standard.WrapMiddleware(cors.Default().Handler))

    g.POST("", createUser)
    g.GET("", getUsers)
    g.GET("/:id", getUser)
}

// Userの作成、一覧、詳細です
func createUser(c echo.Context) error {
    u := new(user)
    if err := c.Bind(u); err != nil {
        return err
    }
    users[u.ID] = *u
    return c.JSON(http.StatusCreated, u)
}

func getUsers(c echo.Context) error {
    return c.JSON(http.StatusOK, users)
}

func getUser(c echo.Context) error {
    return c.JSON(http.StatusOK, users[c.P(0)])
}

GoogleAppEngine用のルーティング

AppEngine用には設定はあまりしなくて良いようです。

app-engine.go
// +build appengine

package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "net/http"
)

func createMux() *echo.Echo {
    e := echo.New()

    // note: we don't need to provide the middleware or static handlers, that's taken care of by the platform
    // app engine has it's own "main" wrapper - we just need to hook echo into the default handler
    s := standard.New("")
    s.SetHandler(e)
    http.Handle("/", s)

    return e
}

// main()も書かなくて良い

設定ファイルも追加します

app.yaml
runtime: go
api_version: go1

handlers:
- url: /
  mime_type: text/html
  static_files: public/index.html
  upload: public/index.html

- url: /favicon.ico
  mime_type: image/x-icon
  static_files: public/favicon.ico
  upload: public/favicon.ico

- url: /scripts
  mime_type: text/javascript
  static_dir: public/scripts

- url: /.*
  script: _go_app

GoogleAppEngineにデプロイ

SDKのインストールは割愛します。ダウンロードはこちらから
https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go

SDKを使ってローカルで起動してみましょう。
ファイル構成はこんな感じです。

.
├── app-engine.go
├── app-standalone.go
├── app.go
├── app.yaml
├── public
│   └── index.html
└── users.go
goapp serve
# can't find import: "github.com/rs/cors" と言われたのでインストールします
go get github.com/rs/cors

# retry
goapp serve
> INFO     2016-04-24 07:25:34,339 admin_server.py:116] Starting admin server at: http://localhost:8000

# 無事に起動したようなので、http://localhost:8080 にアクセスすると <h1>HELLO, ECHO!</h1> と表示されます
# JSONを取得してみます
curl -s http://localhost:8080/users/1 | jq
{
  "id": "1",
  "name": "ジョナサン・ジョースター",
  "age": 20
}

できているようですね。

Googleのクラウドコンソールからプロジェクトを作成し、プロジェクトIDを控えます
https://console.cloud.google.com/

そして、以下のコマンドを実行すると認証画面が出るので許可します。

cd $GOPATH/src
appcfg.py -A <YOUR_PROJECT_ID> -V v1 update echo-sample/
> 04:28 PM Deployment successful.

成功したので、http://YOUR_PROJECT_ID.appspot.com にアクセスして、HELLO,ECHO!と表示されていればOKです。

JSONのAPIも確認してみましょう。

curl -s <YOUR_PROJECT_ID>.appspot.com/users | jq
{
  "1": {
    "id": "1",
    "name": "ジョナサン・ジョースター",
    "age": 20
  },
  "2": {
    "id": "2",
    "name": "ディオ・ブランドー",
    "age": 21
  }
}

まとめ

軽量フレームワークechoを使ってJSONを返すAPIを作成し、GoogleAppEngineで動かしてみました。JSONを返すだけであれば、標準ライブラリのnet/httpでも問題ありませんが、echoは開発が活発で4000starほどついているので試してみました。近々2.0も出るようなので、また試してみたいと思います。