为了账号安全,请及时绑定邮箱和手机立即绑定

在 http.HandleFunc 中记录对传入 HTTP 请求的响应

在 http.HandleFunc 中记录对传入 HTTP 请求的响应

Go
天涯尽头无女友 2021-09-27 14:21:15
这是Go中的后续问题,如何检查写入http.ResponseWriter的http响应?因为那里的解决方案需要伪造请求,这对单元测试很有用,但不适用于实时服务器。我想将我的 Web 服务响应它从用户收到的请求而返回的 HTTP 响应转储到日志文件(或控制台)中。输出应该告诉我标头是什么和 JSON 有效负载。怎么做呢?如果有一个 httputil.DumpResponse 等价物将 http.ResponseWriter 作为参数而不是 http.Response 就完美了,但目前我只能从 http.ResponseWriter 访问 Headerr = 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)
查看完整描述

3 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

中间件链

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


这是一种延续传递风格的形式,您可以像这样编写中间件函数(取自 negroni 的自述文件):


func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

  // do some stuff before

  next(rw, r)

  // do some stuff after

}

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


我们可以以稍微不同的方式实现这个解决方案,而不是一种不太神奇但功能更多的方法(如在函数式编程中)。定义处理程序组合子如下:


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

    }

}

然后将您的链定义为一个组合:


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

现在h是http.HandlerFuncfoo,然后是 bar,然后是 baz。Sink只是一个空的最后一个处理程序,什么都不做(“完成”链。)


将此解决方案应用于您的问题

定义一个处理程序组合子:


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()方法):


func NewDefaultHandler(next http.HandlerFunc) http.HandlerFunc {

    return NewResponseLoggingHandler(NewOtherStuffHandler(next))

}

在此之后,每当您启动这样的链时:


h := NewDefaultHandler(...)

它将自动包含响应日志记录和您在NewDefaultHandler.


查看完整回答
反对 回复 2021-09-27
  • 3 回答
  • 0 关注
  • 207 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信