转到 - 在http.HandleFunc中记录对传入HTTP请求的响应 [英] Go - Logging responses to incoming HTTP requests inside http.HandleFunc

查看:520
本文介绍了转到 - 在http.HandleFunc中记录对传入HTTP请求的响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个后续问题,在go中,如何检查写入http.ResponseWriter的http响应?,因为那里的解决方案需要伪造一个请求,这对单元测试非常有用,但不适用于直播服务器。



我想转储HTTP响应,我的web服务响应它从用户接收到的请求而返回到日志文件(或控制台)中。输出应该告诉我头是什么和JSON有效载荷。



这是怎么回事?

如果有一个httputil.DumpResponse等价物将http.ResponseWriter作为参数而不是http.Response,那么这将是完美的,但目前我只能从http.ResponseWriter访问Header

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

fmt.Printf(r.HandleFunc / path \\\


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

r.ParseForm()

//服务器在这里做一些工作
// ...

//在这里插入调试代码,类似于
//
// dump = http.DumpResponseFromWriter(w)
// fmt.Printf(%s \ n,dump)
});
http.Handle(/ path,r)


解决方案

中间件链接



这个问题的常见解决方案是所谓的中间件链。有几个库提供这种功能,例如



这是一种延续形式,在这里写下你的中间件函数的样式(取自negroni的自述文件):

  func MyMiddleware (rw http.ResponseWriter,r * http.Request,next http.HandlerFunc){
//在
next(rw,r)
之前做一些事情//在$ b之后做一些事
$ b

然后negroni为您提供一个HTTP处理程序,以正确的顺序调用您的中间件。 / p>

我们可以稍微不同的方式实现这个解决方案,这个方法的功能不如神奇功能更强大(如函数式编程)方法。定义处理程序组合子如下:

  func NewFooHandler(next http.HandlerFunc)http.HandlerFunc {
return func(w http://ResponseWriter,r * http.Request){
//在
next(r,w)
之前做一些事情//在
之后做一些事情
}

然后将您的连锁店定义为一个组合:

  h:= NewFooHandler(NewBarHandler(NewBazHandler(Sink)))

现在 h 是一个 http.HandlerFunc ,它会执行foo,然后是bar,然后是baz。 Sink 只是一个空的最后一个处理程序,它什么都不做(用来完成链接。)



应用这个解决您的问题



定义处理程序组合器:

  func NewResponseLoggingHandler (下一个http.HandlerFunc)http.HandlerFunc {
返回func(w http.ResponseWriter,r * http.Request){

//转出记录器的响应编写器
//所有后续处理程序
c:= httptest.NewRecorder()
next(c,r)

//将所有内容从响应记录器
//复制到实际响应编写器
for k,v:= range c.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(c.Code)
c.Body.WriteTo(w)

}
}



现在问题归结为处理程序管理。您可能希望将此处理程序应用于某个类别的所有链。为此,您可以再次使用组合器(这与Negroni的 Classic()方法)相当:

<$ p $
$ func NewDefaultHandler(next http.HandlerFunc)http.HandlerFunc {
return NewResponseLoggingHandler(NewOtherStuffHandler(next))
}

之后,每当你开始这样的连锁时:

  h:= NewDefaultHandler(...)

它会自动包含响应记录和所有默认的东西你在 NewDefaultHandler 中定义的。

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.

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.

How does one go about that?

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)

解决方案

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.

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
}

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

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.)

Applying this solution to your problem

Define a handler combinator:

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)

    }
}

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

It will automatically include response logging and all the default stuff that you defined in NewDefaultHandler.

这篇关于转到 - 在http.HandleFunc中记录对传入HTTP请求的响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆