🗂

【Go】Handle?Handler?HandlerFunc?HandleFunc?ややこしいのでまとめてみた

2024/03/14に公開

これは何

Goのhttpパッケージについて勉強していたときのこと。
「Handle、Handler、HandlerFunc、HandleFuncって名前ややこしすぎん?何が違うの?」 と思ったのでそれぞれの違いをまとめたものです。

この記事のゴール

  • Handle、Handler、HandlerFunc、HandleFuncの違いを理解する。

結論

  • HandleFuncはパスとfunc(ResponseWriter, *Request)を関連づけている。
  • HandlerはHTTPServe(ResponseWriter, *Request)を持つインターフェース。
  • HandlerFuncはfunc(ResponseWriter, *Request)を表す型でHandlerを実装している。
  • HandleはパスとHandlerインターフェースを関連づけている。

HandleFunc

httpパッケージに実装されているHandleFunc関数を見てみます。
第一引数にパス、第二引数にfunc(w http.ResponseWriter, r *http.Request)のシグネチャを持つ関数を受け取ります。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

パスのパターンとハンドラを受け取ってマルチプレクサに登録します。
これによりパスとハンドラが関連づけられています。

実際の使い方

HandleFuncは以下のように使うことができます。

http.HandleFunc("/HandleFunc", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "handle func")
})

また、シグネチャが同じであれば関数を渡すことも可能です。

func hoge(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "handle func")
}
func main() {
    http.HandleFunc("/HandleFunc, hoge)
}

HandlerFunc

HandleFuncに似た名前のものにHandlerFuncがあります。
これの実装を見てみます。

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

HandleFuncは関数だったのに対し、HandlerFuncは型です。
よく考えてみれば、Handleという動詞とHandlerという名詞なので品詞で区別するとわかりやすいかもしれません。
また、ServeHTTPメソッドを実装しているので、Handlerインターフェース(後述)を満たしています。
ServeHTTPが実行されると自身の関数が実行されるようになっています。

Handler

次にHandlerですが、これはServeHTTPメソッドを持つインターフェースです。
先ほどのHandlerFuncがこのインターフェースを満たしていました。

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

Handle

最後にHandleを見てみます。
httpパッケージにおいてHandleは以下のようになっています。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

Handleは第二引数にHandlerインターフェースを取るようになっています。
実装の内容としてはマルチプレクサにパスとhandlerを渡し、双方を関連づけています。
やっていることはHandleFuncと同じですね。
違いは、HandleがHandlerインターフェースを使っているのに対し、HandleFuncはfunc(ResponseWriter, *Request)を使っているという点です。

実際の使い方

Handleを使った実装は以下のようになります。
第二引数をHandlerFunc型でキャストして渡します。

func main() {
	http.Handle("/Handle", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "handle")
	}))
	http.ListenAndServe(":8080", nil)
}

HandlerFuncはHandlerインターフェースを満たしているので、キャストすることによってHandleに渡すことができます。

最後に

少しでも参考になれば幸いです!

GitHubで編集を提案

Discussion