dongyongkui6329 2016-07-12 16:10
浏览 43
已采纳

从外部Go中间件控制HTTP标头

Let's say I have a middleware in Go where I want to override any existing Server headers with my own value.

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Server", serverName)
        h.ServeHTTP(w, r)
    })
}

Then I add it to a response chain like this

http.Handle("/", authHandler)
http.ListenAndServe(":8000", Server(http.DefaultServeMux, "myapp"))

Unfortunately, if the authHandler or anything handled by DefaultServeMux calls w.Header().Add("Server", "foo") (like, say, httputil.ReverseProxy.ServeHTTP), I end up with two Server headers in the response.

$ http localhost:5962/v1/hello_world
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain
Date: Tue, 12 Jul 2016 04:54:04 GMT
Server: inner-middleware
Server: myapp

What I really want is something like this:

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        w.Header().Set("Server", serverName)
    })
}

However, this is disallowed by the semantics of ServeHTTP:

ServeHTTP should write reply headers and data to the ResponseWriter and then return. Returning signals that the request is finished; it is not valid to use the ResponseWriter or read from the Request.Body after or concurrently with the completion of the ServeHTTP call.

I have seen some fairly ugly hacks around this, for example the code in httputil.ReverseProxy.ServeHTTP, which copies headers and rewrites the response code, or using httptest.ResponseRecorder, which reads the entire body into a byte buffer.

I could also reverse the traditional middleware order and put my Server middleware last, somehow, or make it the inner-most middleware.

Is there any simple method I'm missing?

  • 写回答

2条回答 默认 最新

  • dongzhang1875 2016-07-12 17:15
    关注

    You can define a custom type which wraps a ResponseWriter and inserts the Server header just before all headers are written, at the cost of an additional layer of indirection. Here is an example:

    type serverWriter struct {
        w           http.ResponseWriter
        name        string
        wroteHeader bool
    }
    
    func (s serverWriter) WriteHeader(code int) {
        if s.wroteHeader == false {
            s.w.Header().Set("Server", s.name)
            s.wroteHeader = true
        }
        s.w.WriteHeader(code)
    }
    
    func (s serverWriter) Write(b []byte) (int, error) {
        return s.w.Write(b)
    }
    
    func (s serverWriter) Header() http.Header {
        return s.w.Header()
    }
    
    // Server attaches a Server header to the response.
    func Server(h http.Handler, serverName string) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            sw := serverWriter{
                w:           w,
                name:        serverName,
                wroteHeader: false,
            }
            h.ServeHTTP(sw, r)
        })
    }
    

    I wrote more about this here. https://kev.inburke.com/kevin/how-to-write-go-middleware/

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料