AdSense

網頁

2022/7/2

Golang Web middleware 取得response內容

Go middleware取得handler回應內容如HTTP狀態碼(HTTP Status Code)、回應頭(Response Header)、回應主體(Response Body)的方式如下。


如果要取得handler中http.ResponseWriter回應內容,可透過自訂一個實作http.ResponseWriter介面的struct包裹原本的http.ResponseWriter實例並在覆寫的方法中紀錄回應內容於欄位中。

範例環境:

  • Go 1.18


範例

下面建立一個ResponseWriterRecord struct繼承http.ResponseWriter介面,欄位StatusCode紀錄回應狀態碼;Body紀錄回應主體,嵌入欄位為原本的http.ResponseWriter

繼承http.ResponseWriter介面必須實作以下方法:

  • Header() Header - 取得Response Header。直接調用原本的http.ResponseWriter.Header
  • Write([]byte) (int, error) - 寫出Response Body。寫出時將內容存在Body欄位後再透過原本的http.ResponseWriter.Write寫出內容。
  • WriteHeader(statusCode int) - 寫出HTTP回應狀態碼。寫出時將狀態碼存在StatusCode欄位後再透過原本的http.ResponseWriter.WriteHeader寫出狀態碼。

在middleware ResponseRecordHandlerFunc中將原本的http.ResponseWriter替換為ResponseWriterRecord傳入下一個handler,所以傳入HellohandlerFunchttp.ResponseWriter實為ResponseWriterRecord

main.go

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type ResponseWriterRecord struct {
    StatusCode int
    Body       []byte
    http.ResponseWriter
}

func NewResponseWriterRecord(w http.ResponseWriter) *ResponseWriterRecord {
    return &ResponseWriterRecord{ResponseWriter: w}
}

func (r *ResponseWriterRecord) Header() http.Header {
    return r.ResponseWriter.Header()
}

func (r *ResponseWriterRecord) Write(b []byte) (int, error) {
    r.Body = b
    return r.ResponseWriter.Write(b)
}

func (r *ResponseWriterRecord) WriteHeader(statusCode int) {
    r.StatusCode = statusCode
    r.ResponseWriter.WriteHeader(statusCode)
}

func ResponseRecordHandlerFunc(h http.Handler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var rw *ResponseWriterRecord = NewResponseWriterRecord(w)
        h.ServeHTTP(rw, r)

        if rw.StatusCode == 0 {
            rw.StatusCode = http.StatusOK
        }

        header, err := json.Marshal(rw.Header())
        if err != nil {
            panic(err)
        }

        log.Printf("response status code=%d\nheader=%s\nbody=%s",
            rw.StatusCode, string(header), string(rw.Body))
    }
}

func main() {
    http.HandleFunc("/hello", HellohandlerFunc())
    http.HandleFunc("/hi", HiHandlerFunc())

    handler := ResponseRecordHandlerFunc(http.DefaultServeMux)
    http.ListenAndServe(":8080", handler)
}

func HellohandlerFunc() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "text/plain")
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("hello"))
    }
}

func HiHandlerFunc() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "text/plain")
        w.WriteHeader(http.StatusAccepted)
        w.Write([]byte("hi"))
    }
}

github


測試

啟動專案並在命令列以curl執行curl -X GET "http://localhost:8080/hello" -i返回如下。

$ curl -X GET "http://localhost:8080/hello" -i
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sat, 02 Jul 2022 15:52:46 GMT
Content-Length: 5

hello

在console印出以下日誌。

022/07/02 23:54:42 response status code=200, header={"Content-Type":["text/plain"]}, body=hello

接著在命令列以curl執行curl -X GET "http://localhost:8080/hi" -i返回如下。

$ curl -X GET "http://localhost:8080/hi" -i
HTTP/1.1 202 Accepted
Content-Type: text/plain
Date: Sat, 02 Jul 2022 15:55:28 GMT
Content-Length: 2

hi

在console印出以下日誌。

2022/07/02 23:56:59 response status code=202, header={"Content-Type":["text/plain"]}, body=hi



沒有留言:

AdSense