茶漬けの技術メモ

Golang, Rubyで趣味開発します。テックニュース書いたり。ガジェット触ったり。

Go でステータスコードをミドルウェアで取得する

Golangステータスコードミドルウェアで取得するようにしてみます

例えば、ステータスコードで400番台が出たら、slackに通知させたい!

みたいな時に、ミドルウェアステータスコードの中身を見て

何か処理を行いたいとします


前提

routerに gorilla/mux

github.com

middlewareに urfave/negroni

github.com

を使います


サンプルコード
http://localhost:8080/ にアクセスするとHello, World! と返し
path が間違っているとエラーを吐くサンプルを作成してみます

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/urfave/negroni"
)

func main(){
    router := mux.NewRouter()
    router.HandleFunc("/", handleRoot)

    n := negroni.New(
    )
    n.UseHandler(router)

    n.Run(":8080")
}

func handleRoot(w http.ResponseWriter, req *http.Request) {
    if req.URL.Path != "/" {
        http.NotFound(w, req)
        return
    }

    fmt.Fprintf(w, "Hello, World!")
}


negroniでミドルウェアを設定

ミドルウェアを設定します

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/urfave/negroni"
)

func main(){
    router := mux.NewRouter()
    router.HandleFunc("/", handleRoot)

    n := negroni.New(
        newStatusCodeChecker(),
    )
    n.UseHandler(router)

    n.Run(":8080")
}

func handleRoot(w http.ResponseWriter, req *http.Request) {
    if req.URL.Path != "/" {
        http.NotFound(w, req)
        return
    }

    fmt.Fprintf(w, "Hello, World!")
}

func newStatusCodeChecker() negroni.Handler {
    return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        next(w, r)
    })
}

newStatusCodeCheckernext の後に行いたい処理を書きます
今回はミドルウェアの中でもメインの処理が行われた後に処理を行いたいので、 next の後に処理を書くのがポイントです

ミドルウェアについてはこちらの図がとてもわかりやすいです
f:id:biwako_no_otyazuke:20180424012640p:plain:w500


ステータスコードを取得する

先にコードを

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/urfave/negroni"
)

func main(){
    router := mux.NewRouter()
    router.HandleFunc("/", handleRoot)

    n := negroni.New(
        newStatusCodeChecker(),
    )
    n.UseHandler(router)

    n.Run(":8080")
}

func handleRoot(w http.ResponseWriter, req *http.Request) {
    if req.URL.Path != "/" {
        http.NotFound(w, req)
        return
    }

    fmt.Fprintf(w, "Hello, World!")
}

type responseWriterWithStatusCode struct {
    http.ResponseWriter
    statusCode int
}

func (lrw *responseWriterWithStatusCode) WriteHeader(code int) {
    lrw.statusCode = code
    lrw.ResponseWriter.WriteHeader(code)
}

func newStatusCodeChecker() negroni.Handler {
    return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        lrw := newResponseWriterWithStatusCode(w)
        next(lrw, r)
        // next 以降の処理は事後処理のmiddlewareとして実行される
        statusCode := lrw.statusCode
        log.Printf("StatusCoude: %#v", statusCode)
    })
}

func newResponseWriterWithStatusCode(w http.ResponseWriter) *responseWriterWithStatusCode {
    return &responseWriterWithStatusCode{w, http.StatusOK}
}

コードを見ればわかると思うのですが、やっていることとしては

  • responseWriterステータスコードを持っていないので、 statusCode を持った専用の responseWriterWithStatusCode を作成
  • responseWriterWithStatusCodeステータスコードを書き込む WriteHeader を作成
  • responseWriterWithStatusCode をメインの処理に渡す
  • メインの処理が行われた後に、ステータスコードを取得

となります


実行結果
http://localhost:8080/ にアクセスした場合

2018/04/24 01:17:09 StatusCoude: 200


http://localhost:8080/abc にアクセスした場合

2018/04/24 01:17:09 StatusCoude: 404


参考:

[Go] Capturing the HTTP status code from http.ResponseWriter · ndersson.me


おまけ

こちらもどうぞ

o-tyazuke.hatenablog.com


このブログのTwitterアカウントを作成しました!!
フォロー待ってます!!
twitter.com