在Go语言中,如何检查写入http.ResponseWriter的HTTP响应?

huangapple go评论84阅读模式
英文:

In go, how to inspect the http response that is written to http.ResponseWriter?

问题

我正在尝试调试我的Go服务器编写的HTTP响应,可能有一些明显的东西我忽略了。

我看到有一个httputil.DumpResponse可用,但它需要一个http.Response对象,而我只有http.ResponseWriter可用。

有没有办法从http.ResponseWriter中提取http.Response,以便我可以检查响应的内容并输出到控制台或日志中?

上下文:

我正在使用https://github.com/RangelReale/osin和它的默认示例编写一个简单的服务器端身份验证,但我无法理解为什么前端(使用http://ember-simple-auth.com)将身份验证失败(密码错误)解释为成功。

以下是代码片段:

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)
英文:

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

得分: 7

写入一个*httptest.ResponseRecorder(实现了http.ResponseWriter)并进行检查。

包中的示例代码如下:

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())
}

编辑以回答评论中的问题:

如果我理解你的问题正确的话,是的,你可以使用闭包来实现这个。

假设以下是你的处理程序:

func MyHandler(w http.ResponseWriter, r *http.Request) {
	// 做一些有用的事情...
}

然后,你可以将以下闭包注册到你的servemux中,以达到所需的效果:

http.HandleFunc("/my/url", func(w http.ResponseWriter, r *http.Request) {
	// 首先调用MyHandler
	MyHandler(w, r)

	// 然后记录你需要的任何内容
	log.Printf("%#v\n", w)
})

如果这种模式对你有用,那么你可以编写一个高阶方法,将任何func(http.ResponseWriter, *http.Request)包装在这样的闭包中。不过,这是一个单独的主题。

英文:

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.

huangapple
  • 本文由 发表于 2015年1月16日 20:23:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/27983893.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定