In go, how to inspect the http response that is wr

2019-04-25 12:18发布

问题:

There's probably something obvious that I'm missing but I'm trying to debug the HTTP response written by my go server.

I see that there's httputil.DumpResponse available but it takes a http.Response object and what I have available is http.ResponseWriter

Is there a way to extract the http.Response from http.ResponseWriter so I can inspect the content of the response to console or log?

Context:

I'm writing a simple server-side authentication using https://github.com/RangelReale/osin and it's default example, but could not understand why the front-end (using http://ember-simple-auth.com) interprets a failed authentication (incorrect password) as success.

Here's the snippet:

r = mux.NewRouter()
r.HandleFunc("/token", func (w http.ResponseWriter, r *http.Request) {

    fmt.Printf("r.HandleFunc /token\n")

    resp := server.NewResponse()
    defer resp.Close()

    r.ParseForm()

    grantType := r.FormValue("grant_type")
    username := r.FormValue("username")
    password := r.FormValue("password")
    fmt.Printf("/token : grantType=%s  username=%s  password=%s\n", grantType, username, password)

    if ar := server.HandleAccessRequest(resp, r); ar != nil {
        if username == "user" && password == "correct-password" {
            ar.Authorized = true
        } else {
            ar.Authorized = false
        }
        server.FinishAccessRequest(resp, r, ar)
    }

    osin.OutputJSON(resp, w, r)

    // Debug - doesn't work yet
    dump, err := httputil.DumpResponse(w, true)
    if err != nil {
        fmt.Printf("%s\n", dump)
    }
});
http.Handle("/token", r)

回答1:

Write to an *httptest.ResponseRecorder (which implements http.ResponseWriter) and inspect it.

Example from the package:

package main

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

func main() {
    handler := func(w http.ResponseWriter, r *http.Request) {
        http.Error(w, "something failed", http.StatusInternalServerError)
    }

    req, err := http.NewRequest("GET", "http://example.com/foo", nil)
    if err != nil {
        log.Fatal(err)
    }

    w := httptest.NewRecorder()
    handler(w, req)

    fmt.Printf("%d - %s", w.Code, w.Body.String())
}

Edit to answer question in comments:

If I understand your question correctly, then yes, you can make use of closures for this.

Consider the following to be your handler:

func MyHandler(w http.ResponseWriter, r *http.Request) {
    // do useful stuff...
}

You could then register the following closure with your servemux to attain the desired effect:

http.HandleFunc("/my/url", func(w http.ResponseWriter, r *http.Request) {
    // first call MyHandler
    MyHandler(w, r)

    // then log whatever you need
    log.Printf("%#v\n", w)
})

If this pattern proves useful to you then you could write a higher-order method that wraps any func(http.ResponseWriter, *http.Request) in such a closure. That's a topic for itself, though.



标签: http go