何かと分かりにくかった http.HandlerFunc
について、まとめました。
まとめ
-
http.HandlerFunc
は型の名前- 注意: Go 1.9 から使える Type Alias ではない
-
http.HandlerFunc(関数)
とすることで、関数をhttp.Handler
インターフェースを満たした型として扱える - その関数は、
func(http.ResponseWirter, *http.Request)
という形 (シグネチャ) でないといけない
Go 言語の基本をおさらい
下記のように、独自の int
型を作れるように、あるシグネチャを持った関数も独自の型として定義できます。
type MyInt int
type MyFunc func(int, string) string
そうなんです。関数も型にできるんです。
そして、T(x)
(参考: Go言語の仕様) とすることで、型変換ができます。
(T
は変換したい型、x
は変換する型)
func Index(i int, s string) string {
return fmt.Sprintf("Int: %d, String: %s", i, s)
}
testInt := MyInt(1)
testMyFunc := MyFunc(Index)
fmt.Printf("The value is: %v\n", testInt) // The value is: 1
fmt.Printf("The value is: %v\n", testMyFunc(3, "hello")) // The value is: Int: 3, String: hello
どんな型になっているか念のため確認
fmt.Printf("%T\n", testInt) // main.MyInt
fmt.Printf("%T\n", testMyFunc) // main.MyFunc
関数を独自に作った型にでき、関数としても使えることが確認できました。
http.HandlerFunc
を見てみる
type HandlerFunc func(http.ResponseWriter, *http.Request)
となっている。ふむふむ。
ということで、HanlderFunc
は、型である ことが分かります。
また、HandlerFunc
は、func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
のメソッドを持っているので (Go のドキュメント: type Handler)、http.Handler
インターフェースを満たしています。
なので、func(http.ResponseWriter, *http.Request)
という形(シグネチャ)の関数は、http.HandlerFunc(関数)
とすることで、http.Handler
型として扱えるようになるということです。
では、どう使うのか?
middleware として使うことができ、ある処理の前に別の処理を実行させることができます。
下のコードは、画面に hello world
と表示する前に、log.Println
を実行する例
package main
import (
"fmt"
"log"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world")
}
func Log(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("Log is called")
h(w, r)
}
}
func main() {
http.Handle("/", Log(Index))
http.ListenAndServe(":8080", nil)
}
- Log 関数は、
http.HandlerFunc
型を受け取り、http.HandlerFunc
型を返す関数 - Index 関数 は、
http.HandlerFunc
型になるための形(シグネチャ)になっている - なので、Log 関数は、Index 関数を受け取り、独自の処理(
log.Prinln
) のあと、そのまま受け取った関数 (= Index 関数) を実行する
http.HandleFunc
は内部で http.HandlerFunc
を実行している
http.HandleFunc("/", 関数A)
とすることで、トップページへのリクエストがあった際に "関数A" を実行しますが、内部の実装をみると HandlerFunc
が実行されています。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
あー、すっきりした。