以下の記載について考察してみる
HTTPハンドラはHTTPサーバのリクエストを処理しますが、インタフェースによって定義されています。
type Handler interface {
ServerHTTP(http.ResponseWriter, *http.Request)
}
どんな関数でもfunc(http.ResponseWriter, *http.Request)というシグネチャを持っていれば、
http.HandlerFuncに型変換することで、http.Handlerとして使用可能になります`
type HandlerFunc func(http.ResponseWriter, *http.Request)
func (f HandlerFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) {
f(w, r)
これをもうちょっと具体的なサンプルで説明すると以下のようになる
package main
import (
"log"
"net/http"
)
// ただの関数。シグネチャが (http.ResponseWriter, *http.Request)
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello!\n"))
}
func main() {
mux := http.NewServeMux()
// 1) Handler を要求する場所に「関数を型変換して」渡す
mux.Handle("/hello", http.HandlerFunc(hello))
// 2) いったん変数に入れてもOK(Handler として使える)
var h http.Handler = http.HandlerFunc(hello)
mux.Handle("/hello2", h)
// 3) HandleFunc は「関数を直接受け取る」ヘルパ(内部で上の型変換をしてくれる)
mux.HandleFunc("/hello3", hello)
log.Fatal(http.ListenAndServe(":8080", mux))
}
http.HandlerFunc(hello)が型変換であることに注意。
もう少し実用的なサンプルとしては以下のようなものがある。
package main
import (
"log"
"net/http"
"time"
)
// アプリ側の関数型
type AppHandler func(http.ResponseWriter, *http.Request)
// Handler を満たす(これで AppHandler をどこでも http.Handler として使える)
func (h AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h(w, r) }
// ミドルウェアを“メソッド”として重ねがけできる
func (h AppHandler) WithLogging(l *log.Logger) AppHandler {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
h(w, r)
l.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}
}
func (h AppHandler) WithRecover(l *log.Logger) AppHandler {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
l.Printf("panic: %v", rec)
http.Error(w, "internal error", http.StatusInternalServerError)
}
}()
h(w, r)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello!\n"))
}
func main() {
mux := http.NewServeMux()
// 関数 → AppHandler に変換 → WithRecover, WithLoggingメソッドを呼んでデコレート → Handler として登録
h := AppHandler(hello).
WithRecover(log.Default()).
WithLogging(log.Default())
mux.Handle("/hello", h) // h は http.Handler
log.Fatal(http.ListenAndServe(":8080", mux))
}
上述のようにWithRecover, WithLoggingを重ねた場合、各メソッド中のh(w, r) の実態は hello(w, r) となる。