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


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

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

```bash
# テストのフレームワークがないよと怒られるので事前に取得します。
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
チュートリアルに従ってやってみます。

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

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

```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"))
}
```

で、実行。

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

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

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

これに沿って行きます。

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

```go:app.go
package main
// 起点となるファイルです
var e = createMux()
```

```go: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でも作っておきます

```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用には設定はあまりしなくて良いようです。

```go: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()も書かなくて良い
```

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

```yaml: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を使ってローカルで起動してみましょう。
ファイル構成はこんな感じです。

```bash
.
├── app-engine.go
├── app-standalone.go
├── app.go
├── app.yaml
├── public
│   └── index.html
└── users.go
```


```bash
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も確認してみましょう。

```bash
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も出るようなので、また試してみたいと思います。