📌
Goのmiddlewareとは?Handlerをラップして共通処理を組み込む仕組みを理解する
middlewareとは?
handlerの前後に共通処理を追加する仕組み
Goでは、handlerをラップすることで、共通処理(ミドルウェア)を柔軟に差し込めるようになります。例えば、次のような処理がmiddlewareに適してます。
- contextに値を入れる
- ログを出力する
- 認証トークンを検証する
認証ミドルウェアの定義
package middleware
import(
"context"
"net/http"
"myapp/contextkey"
)
// AuthMiddlewareはトークンを確認し、ユーザーIDをcontextに格納する
func AuthMiddleware(next http.Handler) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
// リクエストのヘッダーから認証トークンを取得
token := r.Header.Get("Authorization")
if token == "" || token != "Bearer valid-token"{
// レスポンスにerrorのstatuscodeと文言を書き込み
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
//例userID(実際はDBから取得する)
userID := "user-123"
// contextにuserIdを追加
ctx := context.WithValue(r.Context(), contextkey.UserIDKey,userID)
// 次のhandlerへ渡す
next.ServeHTTP(w, r.WithContext(ctx))
})
}
middlewareをラップして handlerに適用する
mux.Handle("/api/user/profile",
middleware.LoggingMiddleware(
middleware.AuthMiddleware(
http.HandlerFunc(handler.GetUserProfile),
),
),
)
このようにhandlerをネストしていくことで処理を追加できますが、ネストが深くなると可読性が下がるという課題があります。
チェーン構成で読みやすくする
ChainMiddleware関数を定義して、middlewareを順番に適用できるようにすると、コードの見通しが良くなります。
// middlewares...func(http.Handler)http.Handlerは、複数のmiddleware引数を可変超引数として受け取り、各Middlewareはhttp.Handlerを受け取って、http.Handlerを返す関数である必要がありますという意味
func ChainMiddleware(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}
使い方
mux.Handle("/api/user/profile", ChainMiddleware(
http.HandlerFunc(handler.GetUserProfile),
middleware.AuthMiddleware,
middleware.LoggingMiddleware,
))
このように書くことで、処理の流れを上から自然に読めるようになります。
まとめ
Goのmiddlewareは共通処理を責務ごとに分離し、handlerに柔軟に組み込める仕組みです。
Discussion