golang+kocha で Web アプリケーションを作ってみて

簡単な Web アプリケーションを作る機会があったので、前々から興味あった golang で作ってみた。

当初 [revel](https://revel.github.io/) で作ろうとしたが、マイグレーションが自前実装？だったりトランザクションをコントローラに実装する点が個人的に気持ちが悪くて止めた。
RoR っぽいし和製という単純な理由で [kocha](http://naoina.github.io/kocha/) を採用。

やったこと・感じたことを書いていく。

### 環境

* golang 1.4.2
* kocha 0.7.0
* MySQL 5.5.45

## 情報が少ない

golang の Web フレームワーク全体に言えることかも知れないが、とにかく少ない。
和製だからこそ日本語の情報が多いのかなと思ったが甘かった。ドキュメント自体も英語。

## アプリケーション固有の設定

ドキュメントのデータベース設定に関する項で、「[The Twelve-Factor App](http://12factor.net/ja/)」にインスパイアされたと書かれており、環境変数を使って設定を行う。
参照: [Model | Kocha web application framework for Go](http://naoina.github.io/kocha/docs/model.html)

当初、アプリケーション固有の設定をどこに記述して良いか分からず、設定ファイルを用意して読み込ませていた。
The Twelve-Factor App を知ってからは環境変数に記述し、`kocha.Getenv` で取得するようにした。

## データベースのデフォルト文字コードを utf8(mb4) にしてはならない

MySQL+マイグレーションのお話。

デフォルトのストレージエンジンを InnoDB のまま、デフォルトの文字コードを utf8 などにするとマイグレーションが実施されない。

kocha のマイグレーションは、バージョンを管理する為に schema_migration テーブルを作成するのだが、
このテーブルの主キーは VARCHAR(255) であり、InnoDB+utf8 だと 767 byte を越えてインデックスを貼れずにエラーとなる。
ROW_FORMAT を DYNAMIC にして対応しようかと思ったが、SQL がハードコーディングされていたので無理そうだった。
参照: https://github.com/naoina/kocha/blob/37ac3fb2dcdc22508c647d39cd750da6a53fef1d/db.go#L126

ちなみに、schema_migration の作成を失敗しても kocha 上ではエラーならないので注意。

## マイグレーションの書き方

ドキュメントを見ると、雛形の生成で話が終わっていてサンプルが無い。

テーブルを作成するのであれば、恐らく genmai で CreateTable を実行するのがベストだと思う。
参照: https://github.com/gurisugi/kocha-sample/blob/master/scripts/create_table.go#L23

今回は細かく設定したかったので SQL で書いた。

```go
package migration

import "github.com/naoina/genmai"

func (m *Migration) Up_20150806072506_CreateUsersTable(tx *genmai.DB) {
	if _, err := tx.DB().Exec(`
		CREATE TABLE users (
			id INT UNSIGNED NOT NULL AUTO_INCREMENT,
			name VARCHAR(100) NOT NULL,
			created_at DATETIME,
			PRIMARY KEY (id)
		) ENGINE = InnoDB DEFAULT CHARSET 'utf8mb4'
	`); err != nil {
		panic(err)
	}
}

func (m *Migration) Down_20150806072506_CreateUsersTable(tx *genmai.DB) {
	if _, err := tx.DB().Exec(`DROP TABLE users`); err != nil {
		panic(err)
	}
}
```

## ORM で細かいことは出来ない

kocha では genmai という ORM が使われているが、細かい事をするなら内部で使用されている `database/sql` を取り出して使う必要がある。
詳しい理由は、次の参考サイトに分かり易く書かれている。

[Go言語でのORMを色々検討してみた - タオルケット体操](http://hachibeechan.hateblo.jp/entry/2015/03/25/Go%E8%A8%80%E8%AA%9E%E3%81%A7%E3%81%AEORM%E3%82%92%E8%89%B2%E3%80%85%E6%A4%9C%E8%A8%8E%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F)

> もちろんgenmaiにもJOINをビルドするAPIはあるんだけど、ビルドされるフィールドの指定が暗黙的に"テーブル名".*みたいになるため、JOINで持ってきたテーブルのカラムを参照するようなことが出来ない。

まさしくその通り。
現状の ORM で開発は難しく、ほぼ `database/sql` を使って書いてた。

## 共通処理

例えば [revel](https://github.com/revel/revel/blob/83c62046c726f36daec7a28a335521f94b2561a6/skeleton/app/init.go#L33) ではページを表示する際、以下のレスポンスヘッダを返す。

```
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
```

これを kocha でも実現するなら、どこに記述するのかが分からなかった。
色々と考えた結果、Middleware を使えばうまくいきそうだったので試したところうまくいった。

▼レスポンスヘッダ出力例

```go:config/app.go
// ...[略]

type RequestMiddleware struct{}

func (m *RequestMiddleware) Process(app *kocha.Application, c *kocha.Context, next func() error) (err error) {
	header := c.Response.Header()
	header.Set("X-Frame-Options", "SAMEORIGIN")
	header.Set("X-XSS-Protection", "1; mode=block")
	header.Set("X-Content-Type-Options", "nosniff")
	return next()
}

// ...[略]

var (
	AppConfig = &kocha.Config{

		// ...[略]

		// Middlewares.
		Middlewares: []kocha.Middleware{
			&kocha.RequestLoggingMiddleware{},
			&kocha.PanicRecoverMiddleware{},
			&kocha.FormMiddleware{},
			&kocha.SessionMiddleware{

				// ...[略]

			},
			&kocha.FlashMiddleware{},
			&RequestMiddleware{},   // --> 追加
			&kocha.DispatchMiddleware{},
		}
	}
)
```

同様に認証が必要な場合も Middleware を用意すると良い。
今回は Basic 認証を掛けたかったので、専用の Middleware を用意した。

## ページネーションとかそういった類の物はない

kocha 側では用意されていない。
必要なら世に出回っている golang 製のページネーションを持ってくる必要がある。

https://github.com/SayonAB/pagination
https://github.com/gigovich/simpagin

今のうちに使い易いページネーションを作っておくと重宝されるかもしれない。

## まとめにならないまとめ

作成した Web アプリケーションは元気よく [OpenShift](https://www.openshift.com/) 上で動いている。

今回初めて golang で開発をしたが、全体的に Web に関して未成熟な印象を受けた。
これからどんどん発展していくのだろうから、追い続けたいところ。

kocha に関しては、開発を始める上でとても入り易かった。
願わくば日本語のドキュメントが充実するととても嬉しい。

---

元記事: [golang+kocha を使ってみて - 2noの日記](http://2no.hatenablog.com/entry/2015/09/06/225308)