はじめに
最近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言語は公式のドキュメントがしっかりしているので、良いですね。