はじめに
最近Goに入門しました。http.Handle、http.HandleFunc、http.Handler、http.HandlerFuncについてまとめられている記事は溢れていますが、結局何パターンの書き方があるのかまとまっている記事がなかったので、まとめておきます。対象は、筆者と同じぐらいのGo言語初心者を想定しています。
実装
下の5パターンが考えられる全てだと結論付けました。他の方法を知っている方がいらっしゃいましたら教えていただきたいです。
package main
import "net/http"
type one struct {
}
func (h *one) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("one\n"))
}
func two(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("two\n"))
}
func three(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("three\n"))
}
func main() {
http.Handle("/one", &one{})
http.Handle("/two", http.HandlerFunc(two))
// http.Handle("/two", two) <- これはだめ
http.HandleFunc("/three", three)
http.HandleFunc("/four", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("four\n"))
})
http.HandleFunc("/five", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("five\n"))
}))
http.ListenAndServe(":1991", nil)
}
解説
(*それぞれのエンドポイントに対応して、1の実装、2の実装のように表現します。)
順序立てれば、それほど難しいものでもないことが分かると思います。
- エンドポイントを作成する方法は、
http.Handleとhttp.HandleFuncの二つ。 -
http.HandlerFuncはfunc(w http.ResponseWriter, r *http.Request)の別名。ただし、ServeHTTPメソッドが定義されている。 -
http.Handleの第2引数はHandler型で、ServeHTTPを実装している必要がある。 -
http.HandleFuncの第2引数は、結局http.HandlerFuncにキャストされる。
この4つを覚えておけば、理解できると思います。まず、1の実装は自分ので定義したstructにServeHTTPを実装しているので、問題ないです。2つめの実装ですが、単純な関数をhttp.HandlerFunc型にキャストしているので、ServeHTTPが実装され、問題ないです。**逆に言うと、http.Handleの第2引数には、単純なfunc(w http.ResponseWriter, r *http.Request)型の関数を渡すとエラーです。**3つめの実装と、4つめの実装は、無名関数かどうかの違いでしかなく、4.で書いているように、http.HandleFuncにキャストされるため、通常の関数を渡しても問題ないです。もちろん5つ目の実装のように前もってキャストしても問題ないです。
まとめ
- エンドポイントを作成する方法は、
http.Handleとhttp.HandleFuncの二つがある。 -
http.Handleの第2引数は、Handlerインターフェースを実装した(ServeHTTPメソッドが定義されている)ものを渡せば良い。 -
http.HandleFuncの第2引数は、func(w http.ResponseWriter, r *http.Request)型の関数を渡せば良い。
Go言語は公式のドキュメントがしっかりしているので、良いですね。