http Middleware in golang (Handler/HandlerFunc/Handle/HandleFunc)
業務では gRPC を使って開発しているのもあり、net/http パッケージを使用することは少ない。
時々、別プロジェクトで必要になったり、記事で見かける度に理解し直す部分を感じるので、自分用にメモ。
全ては Handler である
まずは結論とコードから。
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
HandlerFunc は ServeHTTP を実装しているので、Handler である。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
DefaultServeMux は Serve によって使用される、デフォルトの ServeMux である。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(w, r) }
ServeMux も ServeHTTP を実装しているので、Handler である。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler)) }
第二引数の func(ResponseWriter, *Request)
は内部で HandlerFunc に変換される。
(HandlerFunc は Handler)
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
ListenAndServe(":8080", nil)
のような形で handler = nil で call されると、DefaultServeMux が使用される。
この前提を持って、Middleware を見ていく。
Middleware
通常
http.Handle("/test", testHandler)
Middleware 使用
http.Handle("/test", Middleware()(testHandler)) func Middleware() func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("before") defer log.Println("after") h.ServeHTTP(w, r) }) } }
- Middleware() は func(http.Handler) http.Handler を返す
- つまり
return func(h http.Handler) http.Handler {}
の部分
- つまり
- Middleware()(handler) とは返ってきた func(http.Handler) http.Handler を実行する
- func(testHandler) http.Handler となっている
- testHandler の前後で処理を行い、
testHandler.ServeHTTP(w, r)
を実行し、 無名関数を http.HandlerFunc() へ変換し、新しいHandler をreturn する。
という流れ。
このつくりだと、1つの Middleware しか受け取れず使い勝手が悪いが、
func (h http.Handler, middlewares ...func(http.Handler) http.Handler)
このような形で複数受け取れる関数を用意し、range などで回すと好きなだけ実行することが可能である。
詳しくは参考リンクを参照されたい。
参考
Writing middleware in #golang and how Go makes it so much fun. | by Mat Ryer | Medium