goa で静的ファイル配信に go-bindata を使う

goa には静的ファイル配信のための DSL として Files が用意されており、以下のような定義を行うと FileServer を生成してくれます。

Files("/swagger/*filepath", "public/swagger/")

生成される FileServerhttp.FileSystem という interface を通してファイルオープンを行うのですが、従来は http.Dir という実装を固定で使う仕様になっていました。 http.Dir は実行環境のネイティブファイルシステムからファイルオープンを行います。これは、ビルドしたバイナリを実行環境にデプロイする際に Files で定義した静的ファイルもそのファイルシステム上に配置する必要がある、ということを意味します。

Go は、ビルドしたバイナリひとつをデプロイすれば動作する、いわゆるシングルバイナリが魅力のひとつですが、 goa の FileServer でそれを実現するためには http.Dir 以外の http.FileSystem 実装を使えた方が都合がいいです。という訳で変更できるようにしました。

github.com

使い方は簡単です。例として go-bindata を使って実装してみましょう。あらかじめ go-bindata で目的の静的ファイルを Go のソースコード化しておき、該当の controller をマウントする前にそのコードを使用するよう FileSystem というフィールドを差し替えます。

c1 := controllers.NewSwaggerController(service)
c1.FileSystem = func(dir string) http.FileSystem {
    return &assetfs.AssetFS{
        Asset:     Asset,
        AssetDir:  AssetDir,
        AssetInfo: AssetInfo,
        Prefix:    dir,
    }
}
app.MountSwaggerController(service, c1)

go-bindata で生成されたコードを http.FileSystem にラップするライブラリとして go-bindata-assetfs を使用しています。これで該当の静的ファイルは Go の内部でオープンされるようになるのでデプロイする必要がなくなります。ちなみに、記事のタイトルと例では go-bindata を取り上げましたが、 FileSystem フィールドの関数は http.FileSystem を返せば良いので他のライブラリを使うこともできます。

より詳細な例は以下をご覧ください。

github.com