LoginSignup
41
41

More than 5 years have passed since last update.

gojiのMiddlewareの使い方

Posted at

gojiはgolangのWebApplicationFrameworkです。

gojiにはMiddlewareという仕組みがあり、これを使うことで、リクエストごとに共通して必要な処理を集約することができます。

gojiはリクエストのロギングを行うMiddlewareが用意されているので、それを参考に見ていきます。

func Logger(c *web.C, h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        reqID := GetReqID(*c)

        printStart(reqID, r)

        lw := mutil.WrapWriter(w)

        t1 := time.Now()
        h.ServeHTTP(lw, r)

        if lw.Status() == 0 {
            lw.WriteHeader(http.StatusOK)
        }
        t2 := time.Now()

        printEnd(reqID, lw, t2.Sub(t1))
    }

    return http.HandlerFunc(fn)
}

上記のLoggerでは、簡単にいうと、以下を行っています。

  • リクエストIDのログ出力(リクエストIDの生成は別のmiddlewareで行われています。)
  • リクエスト自体の処理
  • リクエストIDとともに、処理にかかった時間や返却したHTTPステータスをログ出力

Middlewareはこのように、リクエストを処理する前後に処理を挟み込むことができます。

作り方

Middlewareとして、任意の名称の関数を作ります。func Logger(c *web.C, h http.Handler) http.Handler
middlewareの関数定義はこちら(Mux -> mStack -> mLayer)

その中で、golangでの標準のhttp.HandlerFuncfunc(w http.ResponseWriter, r *http.Request)を実装している関数を返しています。

h.ServeHTTP(r,w)が、リクエスト自体の処理になるので、その前に時間を記録して、その後の時間との差分で処理時間を出しています。

Middlewareは複数登録可能

また、Middlewareは、複数登録が可能です。
gojiでも、DefaultMuxには、以下が登録されています。

  • リクエストIDの生成
  • リクエストログの記録
  • panicリカバリ
  • URL内の文字列をパラメータとして扱う処理

Middlewareは登録順に処理されます。そのため、他のMiddlewareが入っていること前提で処理を作る場合は、順番に気をつけましょう。

特定のパスだけに適用したいMiddlewareがある場合

認証処理をMiddlewareで作った際に、特定のパスには、必要としない場合は、Muxを分離します。例えば、ログインページは、そもそも認証するところなので、認証処理が入っていると、誰もログインできなくなります。

    loginMux := web.New()
    loginMux.Hundle("/login", func(...))

    resourceMux := web.New()
    resourceMux.Hundle("/resource/users", func(...))
    resourceMux.Use(AuthCheck) // AuthCheckは自分で作る認証Middleware

    rootMux := web.DefaultMux
    rootMux.Handle("/login", loginMux)
    rootMux.Handle("/resource/*", resourceMux)

こんな感じで、rootを作って、パスごとにmuxを登録します。(ここはrootの代わりにgoji.Handle()などで登録してもOKです)

この場合、rootに登録されているMiddlewareが実行され、その後それぞれのMuxのMiddlewareの順番に実行されます。そのため、認証処理はresouceのmuxにだけ登録するようにすればOKです。

他にも監視用のエンドポイントだけ特定処理をする/しないといった使い方が出来そうです。

補足

他にも、共通のレスポンスヘッダ挿入や、プロキシ下にいる場合のx-forwarded-forヘッダへの切り替えなど、参考になるMiddlewareがgojiにはあります。

Logger内のmutil.WrapWriter(w)

これは何をしているかというと、通常のhttp.ResponseWriterでは、出力処理を行うところでしか、httpステータスを知ることができません。セットするだけで、後から見る機能がないためです。gojiでは、WrapWriterで、処理をフックして、httpステータスを後から参照できるようにしています。

こういったテクニックは、ソースを読んでみると勉強になります。

41
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
41