在http.HandleFunc内记录对传入HTTP请求的响应 [英] Logging responses to incoming HTTP requests inside http.HandleFunc
问题描述
This is a follow-up question to In go, how to inspect the http response that is written to http.ResponseWriter? since the solution there requires faking a request, which works great for a unit test but not on a live server.
我想将我的Web服务针对从用户收到的请求返回的HTTP响应转储到日志文件(或控制台)中.输出应该告诉我标题是什么以及JSON有效负载.
I would like to dump out HTTP response that my web service is returning in response to requests it receives from users into a log file (or to console). The output should tell me what the headers are and the JSON payload.
那怎么办?
如果有一个使用http.ResponseWriter作为参数而不是http.Response的httputil.DumpResponse等效项,那将是完美的,但是目前我只能从http.ResponseWriter访问标头
If there were a httputil.DumpResponse equivalent that takes a http.ResponseWriter as argument rather than http.Response it would be perfect, but currently I can only access the Header from http.ResponseWriter
r = mux.NewRouter()
r.HandleFunc("/path", func (w http.ResponseWriter, r *http.Request) {
fmt.Printf("r.HandleFunc /path\n")
resp := server.NewResponse()
defer resp.Close()
r.ParseForm()
// Server does some work here
// ...
// Insert debug code here, something like
//
// dump = http.DumpResponseFromWriter(w)
// fmt.Printf("%s\n", dump)
});
http.Handle("/path", r)
推荐答案
中间件链接
一个常见的解决方案是所谓的中间件链.有几个提供此功能的库,例如 negroni .
Middleware Chaining
A common solution to this problem is the so called middleware chain. There are several libraries that provide this functionality e.g. negroni.
这是一种延续传递样式的形式,您可以编写这样的 middleware 函数(取自negroni的自述文件):
It's a form of continuation-passing style where you write your middleware functions like this (taken from negroni's readme):
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// do some stuff before
next(rw, r)
// do some stuff after
}
然后negroni为您提供一个HTTP处理程序,该处理程序以正确的顺序调用您的中间件.
And then negroni gives you an HTTP handler that calls your middlewares in the right order.
我们可以以一种不太神奇且功能更多的方式(如功能编程)来实现该解决方案.定义处理程序组合器,如下所示:
We could implement this solution slightly differently to a less magical and more functional (as in functional programming) approach. Define handler combinators as follows:
func NewFooHandler(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// do some stuff before
next(r,w)
// do some stuff after
}
}
然后将您的链定义为组合:
Then define your chain as a combination:
h := NewFooHandler(NewBarHandler(NewBazHandler(Sink)))
现在,h
是一个http.HandlerFunc
,它执行foo,然后执行bar,然后执行baz. Sink
只是一个空的最后一个处理程序,不执行任何操作(以完成"链条.)
Now h
is an http.HandlerFunc
that does foo, then bar, then baz. Sink
is just an empty last handler, that does nothing (to "finish" the chain.)
定义处理程序组合器:
func NewResponseLoggingHandler(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// switch out response writer for a recorder
// for all subsequent handlers
c := httptest.NewRecorder()
next(c, r)
// copy everything from response recorder
// to actual response writer
for k, v := range c.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(c.Code)
c.Body.WriteTo(w)
}
}
现在问题归结为处理程序管理.您可能希望将此处理程序应用于某个类别中的所有链.为此,您可以再次使用组合器(这在一定程度上等同于negroni的Classic()
方法):
Now the problem boils down to handler management. You'll probably want this handler applied to all chains in a certain category. For this, you can use combinators again (this is somewhat equivalent to negroni's Classic()
method):
func NewDefaultHandler(next http.HandlerFunc) http.HandlerFunc {
return NewResponseLoggingHandler(NewOtherStuffHandler(next))
}
在此之后,每当您启动这样的链条时:
After this, whenever you start a chain like this:
h := NewDefaultHandler(...)
它将自动包括响应日志记录和您在NewDefaultHandler
中定义的所有默认内容.
It will automatically include response logging and all the default stuff that you defined in NewDefaultHandler
.
这篇关于在http.HandleFunc内记录对传入HTTP请求的响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!