僕としてはGoのおすすめのフレームワークを聞かれたら、標準ライブラリのnet/http
と答えるようにしています。というよりも、Goの他のフレームワークと呼ばれているものは、このnet/http
のラッパーでしかないからです。
Goでアプリケーションを作成する場合のイメージは次の通り。battery includedなアプローチは他の言語でもたまにありますが、ついてくる機能が今時のものが多くて、標準ライブラリで済むことが多いです。ウェブ開発についてもそんな感じです。
PythonとかRubyとかもそうですが、言語組み込みのウェブサーバー機能はテスト用で本番運用には機能が足りない、性能が足りない、ということから「プロダクションに耐えうるフレームワークを別に入れないと」と思う人も多いんじゃないかな、と思いますが、Goの場合は組み込みのサーバーで問題なかったりします。Node.jsに近いかも? 世間にはテスト用のはずだったのにやたら性能が高いPHPの内蔵サーバー(ケンオールで有名な会社の社長の作らしい)なんてものもあったりもしますが・・・
Goのウェブを語る上で重要な2つの型
Goのnet/httpでは2つのインタフェースを定義しています。
http.HandlerFunc
http.Handler
前者は、こういうやつ。
func Hello(w http.ResponseWriter, *http.Request) { |
開発者(フレームワークユーザー)がイベントハンドラを実装するときにイベントハンドラが持つべき型ですね。わかりやすいですね。
後者は構造体でも関数でもなんでもいいが、次のメソッドを持っているやつはレスポンスを受け取れるよ、というやつです。
func (r Receiver) ServeHTTP(http.ResponseWriter, *http.Request) { |
実は前者の関数も、http.HandlerFunc(Hello)
とラップしてあげれば、上記のメソッドが生えてhttp.Handler
になる、というのはあるんですが、Goになじみがある人でも、この感覚は持ちにくいところかな、とは思います。今回はこの話は忘れてしまってもいいです。
このServeHTTP()
メソッドが誰が持つかと言うと、標準ライブラリではhttp.ServeMux
、いわゆる「Router」というやつです。Goの標準ライブラリのサーバーhttp.Server
は、このServeHTTP()
を持つもの(ようするにhttp.Handler
)を1つ受け取って、受け取ったリクエストの実際の処理をこいつに委譲します。
通常はhttp.ServeMux
などのRouterを渡します。これにhttp.HandlerFunc
の実際のロジックを登録して、パスごとのロジックを書く、というのがシンプルな状態ですね。
リクエストを受け取ってヘッダーを解析したり、HTTP/2対応だったり、TLSだったりの下回り部分は標準ライブラリで用がすみます。
このServeMux
は他のhttp.Handler
も子供にできるのでネストできます。一部のパスを別のRouterに渡せます。このインタフェースを提供している静的ファイル配信のhttp.FileServerとかhttp.RedirectHandlerとか柔軟に組み合わせられます。
最近のウェブのフレームワークは、ミドルウェアという機構を用意していたりします。リクエストを事前に解釈し、エラー処理をまとめて行ったり、認証チェックをしたり・・・図には書きにくいのですが、これも、http.Handler
として振る舞い、受け取ったリクエストの処理結果を次のhttp.Handler
に渡すラッパーという実装になります。標準ライブラリのhttp.TimeoutHandlerもこれですね。
net/http/httptest
といったテスト用パッケージも、http.Handler
を受け取るローカルテスト用サーバーがいたりします。 猫も杓子もhttp.Handler
です。
他のフレームワークはどうか?
まず、Gorillaとchiは、http.ServeMuxの置き換えて使うRouterを提供しています。置き換えなので、http.Handler
を実装していますし、http.HandlerFunc
もhttp.Handler
も登録できます。サンプルを見てみるとお分かりのように、http.Server
を使って、各ライブラリのRouterを起動するコードになっています。
ハンドラの形式がちょっと特殊っぽいechoはというと、内部ではhttp.Server
を使っています。また、http.Handler
インタフェースは実装しているし、標準ライブラリのhttp.Handler
をラップしてechoの中に持ち込むこともできるので、やろうと思えば標準のサーバーの下の一部だけをEchoにしたり、他のライブラリのハンドラをぶら下げることもできます。
Ginも同様に、http.Server
の上に構築されていますし、それ自身がhttp.Handler
インタフェースを満たしていますし、http.HandlerFunc
などのラッパーで標準形式のハンドラーもかけます。
このhttp.Handler
は他の言語でいうWSGI/Rackとかのような、重要なインタフェースであることがお分かりいただけると思いますし、その上で標準のサーバーが利用上もスタンダードとなっていることがわかるでしょう。
一方で、違いとなっているのが、パスパラメータの切り出しだったり(標準ライブラリでは面倒)、各種ミドルウェアが最初からたくさんついてくるとかの差だったりします。echoは独自のインタフェースのミドルウェアですが、Gorillaはミドルウェアも他のフレームワークから利用できます。
例外はあるのか?
おそらく、fasthttpはnet/httpをラップしてないサーバーなんじゃないかと思います。
fasthttpはnet/httpではなし得ないパフォーマンスを発揮するためにつくられたサーバーです。http.Request
はハンドラーに渡ってくるときには、ヘッダーは完全にパースされてmap[string][]string
に格納されます。一方、fasthttpは不要なパースは避けるために、内部は文字列ですらなく、バイト列で情報を持っています。ヘッダー周りもこのありさまです。
なお、最近Tech Empowerでブイブイ言わせている次のライブラリはみんなこのfasthttpの上に構築されているようです。
こんな感じで特別な事情があれば別実装はありえます。が、ちょっとエクストリームな選択肢ではあると思います。
まとめ
Goでウェブのフレームワークを学ぶ場合、多少の機能差はあれど、どれも言語標準の共通の基盤の上に作られており、自由に組み合わせができることがわかります。net/http
がある意味メタフレームワークとなっています。
net/http
を単独でそのまま使おうとすると、標準のhttp.ServeMux
ではちょっとRouterとして機能が少ないな、とかあります。HTML生成をhtml/template
でやろうとしても、他の言語で慣れた記法とちょっと違って面食らって手が動きにくいということもあります。その場合にはchiとか、Go版mustacheなど、いくつかを好きなサードパーティのライブラリに置き換える方法もあります。
echoやGinでもGorillaでもなんでも使っても問題ありません。どれもnet/http
の兄弟です。こいつらの方が、ミドルウェアなどはたくさんリリースしているので、他の言語ユーザーには親しみやすいかもしれません。これをフレームワークと呼ぶかどうか、便利なライブラリ集と見るかはあなた次第です。