go の middleware を束ねて http.Handler を返す
はじめに
Goで始めるMiddlewareの通り、go の HTTP Server で middleware を通す場合、入れ子を何回も書く必要があって可読性が落ちてしまいがちです。
記事の中で記載されていますが justinas/aliceを利用するとこで middleware をスタックし http.Handler を吐き出すことができますが、個人的にgo-chi/chiのように1行ずつ middleware をスタックした方が見やすいなぁと思い作ってみました。
なぜ http.Handler を返したいか
labstack/echoのように project instance に対して route や middleware をスタックし ListenAndServe() を呼ぶスタイルが多いかと思いますが、OpenAPI などの yaml を読み込んで path の mach で振り分けを行うようなアプリケーションを考えると、既存のフレームワークではうまくハマりませんでした。
middleware を束ねて http.Handler を返す
chainという名前でGithubに置いておきますが中身はとてもシンプルです。
func(http.Handler) http.Handler
として実装された Middleware を次々 Handler にぶっ込んでいます。
package chain import ( "net/http" ) type Middleware func(http.Handler) http.Handler type Chains struct { Handler http.Handler } func NewChain(h http.Handler) *Chains { return &Chains{ Handler: h, } } func (c *Chains) Chain(middleware Middleware) { c.Handler = middleware(c.Handler) }
chain の使い方
cmd 配下にサンプルを載せています。
以下のように Middleware があるとすると
func ExampleMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Println("[START] middleware") next.ServeHTTP(w, r) fmt.Println("[END] middleware") }) }
以下のように ServeHTTP()
を実装した http.Handler を NewChain の引数に渡して middleware を Chain していくことができます。
func (server *Server) Run() { // Chain middleware c := chain.NewChain(server) c.Chain(middleware.ExampleMiddleware) s := &http.Server{ Addr: ":8080", Handler: c.Handler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } if err := s.ListenAndServe(); err != nil { fmt.Println("[error] ", err) } }
まとめ
go で HTTP Server の middleware を束ねて http.Handler を返すライブラリを作ってみました。
作ってみたけどあまり必要ないなこれ。