如何在golang中间件中获取Response statusCode?

问题描述 投票:0回答:6

如何在golang中间件中获取Response statusCode?

ResponseWriter 只有 WriteHeader 接口,找不到 get 接口。

go
6个回答
15
投票

这个方法是可行的。

    type loggingResponseWriter struct {
        http.ResponseWriter
        statusCode int
    }

    func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
        return &loggingResponseWriter{w, http.StatusOK}
    }

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

    func wrapHandlerWithLogging(wrappedHandler http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
            log.Printf("--> %s %s", req.Method, req.URL.Path)

            lrw := NewLoggingResponseWriter(w)
            wrappedHandler.ServeHTTP(lrw, req)

            statusCode := lrw.statusCode
            log.Printf("<-- %d %s", statusCode, http.StatusText(statusCode))
        })
    }

9
投票

使用内格罗尼。它的工作原理与 @huangapple 答案相同,但处理程序实际上实现了所有接口。

import (
    "github.com/urfave/negroni"
)

func wrapHandlerWithLogging(wrappedHandler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        log.Printf("--> %s %s", req.Method, req.URL.Path)

        lrw := negroni.NewResponseWriter(w)
        wrappedHandler.ServeHTTP(lrw, req)

        statusCode := lrw.Status()
        log.Printf("<-- %d %s", statusCode, http.StatusText(statusCode))
    })
}

6
投票

长话短说,您应该自己包装 http.ResponseWriter 或使用库。如果你想自己实现,可以从Negroni源代码

找到一些提示

1
投票

就我而言,我不使用外部库,并且我不想换行

http.ResponseWriter
。我必须在请求的上下文中添加响应的状态,以便稍后在日志记录中使用。因此,我创建了一个小助手来同时在 ResponseWriter 和请求的上下文中写入状态。

type AppContext string
var StatusCode = AppContext("statuCode")

func WriteHeaderAndContext(w http.ResponseWriter, statusCode int, r *http.Request) {
    ctx := context.WithValue(r.Context(), StatusCode, statusCode)
    *r = *(r.WithContext(ctx))

    w.WriteHeader(statusCode)
}

在日志记录中,我将值检索为

r.Context().Value(StatusCode)

缺点发生在被叫方,看起来有点不寻常。例如

WriteHeaderAndContext(w, http.StatusCreated, r)

我们平常做的地方

w.WriteHeader(http.StatusCreated)

0
投票

我也使用了@huangapple答案,但我创建了一个小库,提供了一些设置信息等

https://github.com/joegasewicz/status-writer


0
投票

据我所知,接口

net/http.ResponseWriter
没有任何获取状态代码的公共方法,所以我最终使用定制的结构体包装了ResponseWriter,并在方法
w.WriteHeader(statusCode int)
时接收状态代码被称为。

请记住文档中的这些内容:

如果未显式调用 WriteHeader,则第一次调用 Write 将 触发隐式 WriteHeader(http.StatusOK)。

了解此行为后,我们应该确保如果未调用方法

w.WriteHeader
,状态代码应为 200 OK。这是自定义包装:

type CustomResponseWriter struct {
    responseWriter http.ResponseWriter
    StatusCode     int
}

func ExtendResponseWriter(w http.ResponseWriter) *CustomResponseWriter {
    return &CustomResponseWriter{w, 0}
}

func (w *CustomResponseWriter) Write(b []byte) (int, error) {
    return w.responseWriter.Write(b)
}

func (w *CustomResponseWriter) Header() http.Header {
    return w.responseWriter.Header()
}

func (w *CustomResponseWriter) WriteHeader(statusCode int) {
    // receive status code from this method
    w.StatusCode = statusCode
    w.responseWriter.WriteHeader(statusCode)

    return
}

func (w *CustomResponseWriter) Done() {
    // if the `w.WriteHeader` wasn't called, set status code to 200 OK
    if w.StatusCode == 0 {
        w.StatusCode = http.StatusOK
    }

    return
}

要在中间件中使用它,请或多或少像这样使用它:

func logMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ew := ExtendResponseWriter(w)

        next.ServeHTTP(ew, r)

        ew.Done()

        fmt.Printf("%s %s %s", r.Method, r.URL.String(), ew.StatusCode)
    })
}
© www.soinside.com 2019 - 2024. All rights reserved.