oinume journal

Scratchpad of what I learned

Goのhttpパッケージだけでmiddlewareを実装する

標準ライブラリのhttpパッケージだけでもmiddlewareは簡単に作れますよ、というお話。

おさらい: http.Handlerまたはhttp.HandlerFuncでやり取りする

Goのhttp.Handlerやhttp.HandlerFuncをちゃんと理解する - oinume journalに書いたとおり、

  • http.ListenAndServe
  • ServeMux.Handle

に渡すのは http.Handler なので、何かしらのmiddlewareもこのinterfaceを満たすことを考えればよい。もしくは ServeMux.HandleFunc を使うのであれば http.HandlerFunc を使うでもよし。

middlewareはhttp.Handlerを引数に取り、http.Handlerを返す

例えば、 admin という名前のCookieを持っていないとアクセスできないようなmiddlewareを考えてみる。

func requireAdminCookie(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        _, err := r.Cookie("admin") // Check `admin` cookie
        if err != nil {
            http.Error(w, "No admin cookie", http.StatusForbidden)
            return
        }
        h.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

こんな感じで、h.ServeHTTPを呼ぶ前にCookieがあるかをチェックする処理を入れる。

このmiddlewareを使って、「特定のURLはadmin cookieがないとアクセスできないようにする」ためには、以下のようにmiddlewareと実際のHandlerを組み合わせる必要がある。以下のコードだと /admin のURLはCookieがないと403 Forbiddenが返される。

func main() {
    mux := http.NewServeMux()
    mux.Handle("/admin", requireAdminCookie(http.HandlerFunc(handleAdmin)))
    if err := http.ListenAndServe(":8080", mux); err != nil {
        log.Fatal(err)
    }
}

// responseを返すHandler
func handleAdmin(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    fmt.Fprint(w, "This is admin page")
}

参考リンク

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】