APIサーバーを開発する際、複数のMiddlewareを実装しなければならない場合があると思います。普通に実装すると、以下のようになると思います。
router.go
func NewRouter() *http.ServeMux {
mux := http.NewServeMux()
h := handler.NewHandler()
mux.Handle("/", middleware.Middleware1(
middleware.Middleware2(
middleware.Middleware3(
h))))
...
return mux
}
このように、通常の書き方ではMiddlewareの数が増えれば増える程ネストが深くなっていき、可読性が損なわれていきます。今回は、Middlewareの数が増えても可読性をある程度確保できないかと思い、調査をしてみました。
解決策
解決策を一言で表すと、「Middlewareをチェインする関数を別途用意する」になります。
utils.go
// Middleware型を定義
type Middleware func(http.Handler) http.Handler
// Middleware型のスライスでハンドラーをWrap
func ChainMiddleware(h http.Handler, ms ...Middleware) http.Handler {
if len(ms) < 1 {
return h
}
wrapped := h
for i := len(ms) - 1; i >= 0; i-- {
wrapped = ms[i](wrapped)
}
return wrapped
}
この用意した関数を利用すると、下記のようにルーティングを実装できます。
router.go
func NewRouter() *http.ServeMux {
mux := http.NewServeMux()
h := handler.NewHandler()
mux.Handle("/", middleware.ChainMiddleware(
h,
middleware.Middleware1,
middleware.Middleware2,
middleware.Middleware3,
)
...
return mux
}
コードの量自体にはほとんど変化がありませんが、可読性は上がっていると思います。さらに、複数のパターンで全く同じMiddlewareが呼び出される場合には、Middlewareのスライスを変数に格納することで、コードの量を大幅に減らすことができます。
router.go
func NewRouter() *http.ServeMux {
mux := http.NewServeMux()
h := handler.NewHandler()
ms := []middleware.Middleware{
middleware.Middleware1,
middleware.Middleware2,
middleware.Middleware3
}
mux.Handle("/", middleware.ChainMiddleware(h, ms)
mux.Handle("/p1", middleware.ChainMiddleware(h, ms)
mux.Handle("/p2", middleware.ChainMiddleware(h, ms)
...
return mux
}
注意点
ただし、呼び出されるMiddlewareの順番は、スライスに格納される順番に依存するので、ネストが浅い順にスライスに格納する必要があります。上記の例を参考にしてみて下さい。
参考にした記事