Goで始めるMiddleware

そもそもMiddlewareとは何かと言うと、私の認識ではアプリケーションの処理の前後で何らかの処理を行ったりするもの。例えば、ログを追加したりリクエストのAPIキーを認証したりする。以下の図がわかりやすい。

f:id:takayukinakata:20171124184420p:plain

Laravel 5.0 - Middleware (Filter-style) | MattStauffer.com より引用

とはいえ、上記だけでは具体的にどのように実装したらイメージがつかなかったんだけど、Goの公式のwikiでいろいろ紹介していてわかりやすかったのでこれを元に簡単なサンプルを実装した。

LearnServerProgramming · golang/go Wiki · GitHub

Middlewares in Go: Best practices and examples – Nicolas Merouze

package main

import (
    "fmt"
    "net/http"
    "time"
)

// indexHandler ...
func indexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Hello, middleware!")
}

// aboutHandler ...
func aboutHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("This is midlleware test!!")
}

// loggingHandler ...
func loggingHandler(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        t1 := time.Now()
        next.ServeHTTP(w, r)
        t2 := time.Now()
        fmt.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1))
    }
}

func main() {
    http.HandleFunc("/", loggingHandler(indexHandler))
    http.HandleFunc("/about", loggingHandler(aboutHandler))
    http.ListenAndServe(":8888", nil)
}

ポイントは、

  • http.HandlerFuncを返すラッパーを作ること
  • ラッパーの中で引数のhttp.HandlerFuncを呼び出すこと

だと個人的に思った。前述の図との対応としては、処理のイメージとしてはこんな感じだろうか。Middlewareで前処理を行って、アプリケーションの処理( next.ServeHTTP(w, r) )を行って、Middlewareで後処理を行う。

// sampleHandler ...
func sampleHandler(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 前処理
        next.ServeHTTP(w, r)
        // 後処理
    }
}

まとめ

Middlewareは怖くない