読者です 読者をやめる 読者になる 読者になる

2noの日記

メモ用

golang+kocha を使ってみて

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

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

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

環境

情報が少ない

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

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

ドキュメントのデータベース設定に関する項で、「The Twelve-Factor App」にインスパイアされたと書かれており、環境変数を使って設定を行う。
参照: Model | Kocha web application framework for Go

当初、アプリケーション固有の設定をどこに記述して良いか分からず、設定ファイルを用意して読み込ませていた。
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 で書いた。

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 を取り出して使う必要がある。
詳しい理由は、次の参考サイトに分かり易く書かれている。

hachibeechan.hateblo.jp

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

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

共通処理

例えば revel ではページを表示する際、以下のレスポンスヘッダを返す。

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

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

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

// 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 製のページネーションを持ってくる必要がある。

github.com

github.com

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

まとめにならないまとめ

作成した Web アプリケーションは元気よく OpenShift 上で動いている。

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

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